diff --git a/.gitattributes b/.gitattributes index 52b735b3..9b4c18b0 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ **/*.dll filter=lfs diff=lfs merge=lfs -text **/*.so filter=lfs diff=lfs merge=lfs -text **/*.dylib filter=lfs diff=lfs merge=lfs -text +**/*.jpg filter=lfs diff=lfs merge=lfs -text livekit-protocol/livekit/protocol/** linguist-generated=true livekit-rtc/livekit/rtc/_proto/** linguist-generated=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..11a37c27 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,4 @@ +# CODEOWNERS for python-sdks +# These owners will be requested for review on all PRs + +* @cloudwebrtc @lukasIO @xianshijing-lk diff --git a/.github/banner_dark.png b/.github/banner_dark.png index a80f87f1..766f636c 100644 Binary files a/.github/banner_dark.png and b/.github/banner_dark.png differ diff --git a/.github/banner_light.png b/.github/banner_light.png index de0d1dbd..02539b12 100644 Binary files a/.github/banner_light.png and b/.github/banner_light.png differ diff --git a/.github/update_versions.py b/.github/update_versions.py new file mode 100644 index 00000000..691fd8a5 --- /dev/null +++ b/.github/update_versions.py @@ -0,0 +1,149 @@ +import pathlib +import re +import click +from packaging.version import Version + +PACKAGES = { + "livekit": "livekit-rtc/livekit/rtc/version.py", + "livekit-api": "livekit-api/livekit/api/version.py", + "livekit-protocol": "livekit-protocol/livekit/protocol/version.py", +} + +TAG_PREFIXES = { + "livekit": "rtc", + "livekit-api": "api", + "livekit-protocol": "protocol", +} + + +def _esc(*codes: int) -> str: + return "\033[" + ";".join(str(c) for c in codes) + "m" + + +def read_version(f: pathlib.Path) -> str: + text = f.read_text() + m = re.search(r'__version__\s*=\s*[\'"]([^\'"]+)[\'"]', text) + if not m: + raise ValueError(f"could not find __version__ in {f}") + return m.group(1) + + +def write_new_version(f: pathlib.Path, new_version: str) -> None: + text = f.read_text() + new_text = re.sub( + r'__version__\s*=\s*[\'"][^\'"]*[\'"]', + f'__version__ = "{new_version}"', + text, + count=1, + ) + f.write_text(new_text) + + +def bump_version(cur: str, bump_type: str) -> str: + v = Version(cur) + if bump_type == "release": + return v.base_version + if bump_type == "patch": + return f"{v.major}.{v.minor}.{v.micro + 1}" + if bump_type == "minor": + return f"{v.major}.{v.minor + 1}.0" + if bump_type == "major": + return f"{v.major + 1}.0.0" + raise ValueError(f"unknown bump type: {bump_type}") + + +def bump_prerelease(cur: str, bump_type: str) -> str: + v = Version(cur) + base = v.base_version + if bump_type == "rc": + if v.pre and v.pre[0] == "rc": + next_rc = v.pre[1] + 1 + else: + next_rc = 1 + return f"{base}.rc{next_rc}" + raise ValueError(f"unknown prerelease bump type: {bump_type}") + + +def update_api_protocol_dependency(new_protocol_version: str) -> None: + """Update livekit-api's dependency on livekit-protocol.""" + pyproject = pathlib.Path("livekit-api/pyproject.toml") + if not pyproject.exists(): + return + old_text = pyproject.read_text() + new_text = re.sub( + r'"livekit-protocol>=[\w.\-]+,', + f'"livekit-protocol>={new_protocol_version},', + old_text, + ) + if new_text != old_text: + pyproject.write_text(new_text) + print(f"Updated livekit-api dependency on livekit-protocol to >={new_protocol_version}") + + +def do_bump(package: str, bump_type: str) -> None: + version_path = PACKAGES[package] + vf = pathlib.Path(version_path) + cur = read_version(vf) + new = bump_version(cur, bump_type) + print(f"{package}: {_esc(31)}{cur}{_esc(0)} -> {_esc(32)}{new}{_esc(0)}") + write_new_version(vf, new) + + if package == "livekit-protocol": + update_api_protocol_dependency(new) + + +def do_prerelease(package: str, prerelease_type: str) -> None: + version_path = PACKAGES[package] + vf = pathlib.Path(version_path) + cur = read_version(vf) + new = bump_prerelease(cur, prerelease_type) + print(f"{package}: {_esc(31)}{cur}{_esc(0)} -> {_esc(32)}{new}{_esc(0)}") + write_new_version(vf, new) + + if package == "livekit-protocol": + update_api_protocol_dependency(new) + + +@click.command() +@click.option( + "--package", + type=click.Choice(list(PACKAGES.keys())), + required=True, + help="Package to bump.", +) +@click.option( + "--pre", + type=click.Choice(["rc", "none"]), + default="none", + help="Pre-release type.", +) +@click.option( + "--bump-type", + type=click.Choice(["patch", "minor", "major", "release"]), + default="patch", + help="Type of version bump.", +) +@click.option( + "--print-version", + is_flag=True, + default=False, + help="Print current version and tag prefix, don't bump.", +) +def bump(package: str, pre: str, bump_type: str, print_version: bool) -> None: + if print_version: + vf = pathlib.Path(PACKAGES[package]) + version = read_version(vf) + tag_prefix = TAG_PREFIXES[package] + # Output as key=value for easy parsing + print(f"version={version}") + print(f"tag_prefix={tag_prefix}") + return + + if pre == "none": + do_bump(package, bump_type) + else: + do_prerelease(package, pre) + + +if __name__ == "__main__": + bump() diff --git a/.github/workflows/build-api.yml b/.github/workflows/build-api.yml index 5f47f74d..9f4691f5 100644 --- a/.github/workflows/build-api.yml +++ b/.github/workflows/build-api.yml @@ -26,18 +26,18 @@ jobs: run: working-directory: ${{ env.PACKAGE_DIR }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true - - uses: actions/setup-python@v4 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 - name: Build wheel & sdist run: | pip3 install build wheel python3 -m build --wheel --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: api-release path: | @@ -48,19 +48,17 @@ jobs: name: Publish API release needs: build_wheels runs-on: ubuntu-latest + environment: pypi permissions: id-token: write - if: startsWith(github.ref, 'refs/tags/api-v') + if: startsWith(github.ref, 'refs/tags/api-v') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: api-release path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 docs: needs: [publish] diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml index 99bd94ec..e988444c 100644 --- a/.github/workflows/build-docs.yml +++ b/.github/workflows/build-docs.yml @@ -1,12 +1,4 @@ -# This workflow will upload a Python Package using Twine when a release is created -# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries - -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -name: Build Docs +name: Build Docs on: workflow_dispatch: @@ -45,29 +37,36 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: recursive - - name: Install Package to Document - run: python -m pip install ${{ inputs.package_dir }}/ + - name: Install Package to Document + env: + PACKAGE_DIR: ${{ inputs.package_dir }} + run: python -m pip install "$PACKAGE_DIR/" - - name: Download ffi - run: | - if [[ '${{ inputs.package_name }}' = 'livekit.rtc' ]]; then - pip install requests - python livekit-rtc/rust-sdks/download_ffi.py --output $(python -m site --user-site)/livekit/rtc/resources - fi + - name: Download ffi + env: + PACKAGE_NAME: ${{ inputs.package_name }} + run: | + if [[ "$PACKAGE_NAME" = 'livekit.rtc' ]]; then + pip install requests + python livekit-rtc/rust-sdks/download_ffi.py --output "$(python -m site --user-site)/livekit/rtc/resources" + fi - - name: Install pdoc - run: pip install --upgrade pdoc + - name: Install pdoc + run: pip install --upgrade pdoc - - name: Build Docs - run: python -m pdoc ${{ inputs.package_name }} --docformat=google --output-dir docs + - name: Build Docs + env: + PACKAGE_NAME: ${{ inputs.package_name }} + run: python -m pdoc "$PACKAGE_NAME" --docformat=google --output-dir docs - - name: S3 Upload - run: aws s3 cp docs/ s3://livekit-docs/${{ inputs.package_dir }} --recursive - env: - AWS_ACCESS_KEY_ID: ${{ secrets.DOCS_DEPLOY_AWS_ACCESS_KEY }} - AWS_SECRET_ACCESS_KEY: ${{ secrets.DOCS_DEPLOY_AWS_API_SECRET }} - AWS_DEFAULT_REGION: "us-east-1" \ No newline at end of file + - name: S3 Upload + env: + PACKAGE_DIR: ${{ inputs.package_dir }} + AWS_ACCESS_KEY_ID: ${{ secrets.DOCS_DEPLOY_AWS_ACCESS_KEY }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.DOCS_DEPLOY_AWS_API_SECRET }} + AWS_DEFAULT_REGION: "us-east-1" + run: aws s3 cp docs/ "s3://livekit-docs/$PACKAGE_DIR" --recursive diff --git a/.github/workflows/build-protocol.yml b/.github/workflows/build-protocol.yml index 23ff014f..55032ce2 100644 --- a/.github/workflows/build-protocol.yml +++ b/.github/workflows/build-protocol.yml @@ -28,28 +28,37 @@ jobs: run: working-directory: ${{ env.PACKAGE_DIR }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true ref: ${{ github.event.pull_request.head.ref }} - - uses: actions/setup-python@v4 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 - name: Install Protoc - uses: arduino/setup-protoc@v2 + # protoc 29.x emits gencode 5.x. Do NOT bump to 30+ (gencode 6+): the + # protobuf runtime is capped at <7 by livekit-agents deps + # (opentelemetry-proto, grpcio-tools), and generate_proto.sh enforces a + # gencode<=5 guard. gencode 5 loads on any runtime >=5 (5/6/7). See the + # comment block in livekit-protocol/generate_proto.sh. + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b # v3 with: - version: "25.3" + version: "29.3" repo-token: ${{ secrets.GITHUB_TOKEN }} - name: generate python stubs run: ./generate_proto.sh - name: Add changes - uses: EndBug/add-and-commit@v9 + uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10 with: add: '["livekit-protocol/"]' default_author: github_actions message: generated protobuf + # Skip the default `git fetch --tags --force`: on this repo it pulls + # 100+ refs at once and the action aborts before committing. Committing + # generated stubs onto the PR head branch doesn't need a full fetch. + fetch: false build_wheels: name: Build Protocol wheel/sdist @@ -58,18 +67,18 @@ jobs: run: working-directory: ${{ env.PACKAGE_DIR }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true - - uses: actions/setup-python@v4 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 - name: Build wheel & sdist run: | pip3 install build wheel python3 -m build --wheel --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: protocol-release path: | @@ -80,19 +89,17 @@ jobs: name: Publish Protocol release needs: build_wheels runs-on: ubuntu-latest + environment: pypi permissions: id-token: write - if: startsWith(github.ref, 'refs/tags/protocol-v') + if: startsWith(github.ref, 'refs/tags/protocol-v') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: name: protocol-release path: dist - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 docs: needs: [publish] diff --git a/.github/workflows/build-rtc.yml b/.github/workflows/build-rtc.yml index ac58b75b..d0dad94f 100644 --- a/.github/workflows/build-rtc.yml +++ b/.github/workflows/build-rtc.yml @@ -19,6 +19,42 @@ env: PACKAGE_DIR: ./livekit-rtc jobs: + generate_protobuf: + runs-on: ubuntu-latest + name: Generating protobuf + if: github.event_name == 'pull_request' + + defaults: + run: + working-directory: ${{ env.PACKAGE_DIR }} + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + ref: ${{ github.event.pull_request.head.ref }} + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + + - name: Install Protoc + uses: arduino/setup-protoc@c65c819552d16ad3c9b72d9dfd5ba5237b9c906b # v3 + with: + version: "25.1" + repo-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Install deps + run: | + pip3 install mypy-protobuf==3.6.0 "protobuf>=4.25.0,<5.0.0" + + - name: generate python stubs + run: ./generate_proto.sh + + - name: Add changes + uses: EndBug/add-and-commit@290ea2c423ad77ca9c62ae0f5b224379612c0321 # v10 + with: + add: '["livekit-rtc/"]' + default_author: github_actions + message: generated protobuf + build_wheels: name: Build RTC wheels (${{ matrix.archs }}) runs-on: ${{ matrix.os }} @@ -29,7 +65,7 @@ jobs: include: - os: ubuntu-latest archs: x86_64 - - os: buildjet-4vcpu-ubuntu-2204-arm + - os: namespace-profile-default-arm64 archs: aarch64 - os: windows-latest archs: AMD64 @@ -39,28 +75,23 @@ jobs: run: working-directory: ${{ env.PACKAGE_DIR }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true - - uses: actions/setup-python@v4 - - - name: Install cibuildwheel - if: runner.os != 'macOS' - run: python3 -m pip install cibuildwheel==2.17.0 - - - name: Install cibuildwheel on macOS - if: runner.os == 'macOS' - run: python3 -m pip install --break-system-packages cibuildwheel==2.17.0 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + id: setup-python + with: + python-version: "3.11" - name: Build wheels - run: python3 -m cibuildwheel --output-dir dist + run: pipx run --python '${{ steps.setup-python.outputs.python-path }}' cibuildwheel==3.3.1 --output-dir dist env: CIBW_ARCHS: ${{ matrix.archs }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: rtc-release + name: rtc-release-${{ matrix.os }} path: livekit-rtc/dist/*.whl make_sdist: @@ -70,7 +101,7 @@ jobs: run: working-directory: ${{ env.PACKAGE_DIR }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true @@ -79,28 +110,84 @@ jobs: pip3 install build python3 -m build --sdist - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: - name: rtc-release + name: rtc-release-sdist path: livekit-rtc/dist/*.tar.gz + + test: + name: Test (${{ matrix.os }}, Python ${{ matrix.python-version }}) + needs: [build_wheels] + strategy: + fail-fast: false + matrix: + include: + # Linux x86_64 tests + - os: ubuntu-latest + python-version: "3.9" + artifact: rtc-release-ubuntu-latest + - os: ubuntu-latest + python-version: "3.10" + artifact: rtc-release-ubuntu-latest + - os: ubuntu-latest + python-version: "3.11" + artifact: rtc-release-ubuntu-latest + - os: ubuntu-latest + python-version: "3.12" + artifact: rtc-release-ubuntu-latest + - os: ubuntu-latest + python-version: "3.13" + artifact: rtc-release-ubuntu-latest + - os: ubuntu-latest + python-version: "3.14" + artifact: rtc-release-ubuntu-latest + # macOS tests (arm64 runner) + - os: macos-latest + python-version: "3.9" + artifact: rtc-release-macos-latest + - os: macos-latest + python-version: "3.12" + artifact: rtc-release-macos-latest + - os: macos-latest + python-version: "3.14" + artifact: rtc-release-macos-latest + # Windows tests + - os: windows-latest + python-version: "3.9" + artifact: rtc-release-windows-latest + - os: windows-latest + python-version: "3.12" + artifact: rtc-release-windows-latest + - os: windows-latest + python-version: "3.14" + artifact: rtc-release-windows-latest + uses: ./.github/workflows/tests.yml + with: + os: ${{ matrix.os }} + python-version: ${{ matrix.python-version }} + artifact-name: ${{ matrix.artifact }} + secrets: + LIVEKIT_URL: ${{ secrets.LIVEKIT_URL }} + LIVEKIT_API_KEY: ${{ secrets.LIVEKIT_API_KEY }} + LIVEKIT_API_SECRET: ${{ secrets.LIVEKIT_API_SECRET }} + publish: name: Publish RTC release - needs: [build_wheels, make_sdist] + needs: [build_wheels, make_sdist, test] runs-on: ubuntu-latest + environment: pypi permissions: id-token: write - if: startsWith(github.ref, 'refs/tags/rtc-v') + if: startsWith(github.ref, 'refs/tags/rtc-v') && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.fork == false) steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 with: - name: rtc-release + pattern: rtc-release-* path: dist + merge-multiple: true - - uses: pypa/gh-action-pypi-publish@release/v1 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} + - uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 docs: needs: [publish] diff --git a/.github/workflows/check-types.yml b/.github/workflows/check-types.yml index 2ebfa830..1018cfc0 100644 --- a/.github/workflows/check-types.yml +++ b/.github/workflows/check-types.yml @@ -1,4 +1,4 @@ -name: Check Types +name: Check Types on: push: @@ -13,23 +13,29 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - with: - submodules: recursive - - - name: Set up Python 3.9 - uses: actions/setup-python@v2 - with: - python-version: 3.9 - - - name: Download ffi - run: python -m pip install requests && python livekit-rtc/rust-sdks/download_ffi.py --output $(python -m site --user-site)/livekit/rtc/resources - - - name: Install mypy - run: python -m pip install --upgrade mypy - - - name: Install packages - run: python -m pip install ./livekit-api ./livekit-protocol ./livekit-rtc - - - name: Check Types - run: python -m mypy --install-type --non-interactive -p 'livekit-protocol' -p 'livekit-api' -p 'livekit-rtc' \ No newline at end of file + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: recursive + + - name: Set up Python 3.9 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: 3.9 + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Install the project + run: uv sync --all-extras --dev + + - name: Install workspace packages + run: uv pip install -e livekit-protocol -e livekit-api -e livekit-rtc + + - name: Download ffi + run: uv run python livekit-rtc/rust-sdks/download_ffi.py --output .venv/lib/python3.9/site-packages/livekit/rtc/resources + + - name: Check Types + run: uv run mypy livekit-protocol livekit-api livekit-rtc diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 00000000..7263234a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,411 @@ +name: Publish to PyPI + +on: + # Step 1: Dispatch creates a version bump PR + workflow_dispatch: + inputs: + package: + description: "Package to publish" + type: choice + required: true + options: + - livekit + - livekit-api + - livekit-protocol + version: + description: "What to publish" + type: choice + required: true + options: + - "patch (1.5.1 → 1.5.2)" + - "minor (1.5.1 → 1.6.0)" + - "major (1.5.1 → 2.0.0)" + - "patch-rc (1.5.1 → 1.5.2.rc1)" + - "minor-rc (1.5.1 → 1.6.0.rc1)" + - "major-rc (1.5.1 → 2.0.0.rc1)" + - "next-rc (.rc1 → .rc2)" + - "promote (1.6.0.rc2 → 1.6.0)" + branch: + description: "Branch to publish from (default: main)" + type: string + required: false + default: "main" + + # Step 2: Merging the release PR triggers build + publish + pull_request: + types: [closed] + +permissions: + contents: write + pull-requests: write + id-token: write + +jobs: + # ── Step 1: Create a version bump PR ────────────────────────── + bump: + name: Create release PR + if: github.event_name == 'workflow_dispatch' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + ref: ${{ inputs.branch }} + submodules: true + + - name: Guard non-main branches + env: + INPUT_VERSION: ${{ inputs.version }} + INPUT_BRANCH: ${{ inputs.branch }} + run: | + key=$(echo "$INPUT_VERSION" | awk '{print $1}') + if [ "$INPUT_BRANCH" != "main" ]; then + case "$key" in + *-rc|next-rc) ;; # allowed + *) echo "::error::Only RC releases are allowed from non-main branches (got '$key' on '$INPUT_BRANCH')"; exit 1 ;; + esac + fi + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: "3.10" + + - name: Install dependencies + run: pip install click packaging + + - name: Bump version + env: + INPUT_VERSION: ${{ inputs.version }} + INPUT_PACKAGE: ${{ inputs.package }} + run: | + key=$(echo "$INPUT_VERSION" | awk '{print $1}') + pkg="$INPUT_PACKAGE" + + case "$key" in + patch|minor|major) + python .github/update_versions.py --package "$pkg" --bump-type "$key" + ;; + patch-rc|minor-rc|major-rc) + bump="${key%-rc}" + python .github/update_versions.py --package "$pkg" --bump-type "$bump" + python .github/update_versions.py --package "$pkg" --pre rc + ;; + next-rc) + python .github/update_versions.py --package "$pkg" --pre rc + ;; + promote) + python .github/update_versions.py --package "$pkg" --bump-type release + ;; + esac + + - name: Read new version + id: version + env: + INPUT_PACKAGE: ${{ inputs.package }} + run: | + eval "$(python .github/update_versions.py --package "$INPUT_PACKAGE" --print-version)" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "tag_prefix=$tag_prefix" >> "$GITHUB_OUTPUT" + echo "Package: $INPUT_PACKAGE, New version: $version, Tag: ${tag_prefix}-v${version}" + + - name: Close existing release PRs for this package + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_PREFIX: ${{ steps.version.outputs.tag_prefix }} + run: | + prefix="release/${TAG_PREFIX}-v" + gh pr list --state open --json number,headRefName \ + --jq ".[] | select(.headRefName | startswith(\"$prefix\")) | .number" | while read -r pr; do + echo "Superseding release PR #$pr" + gh pr comment "$pr" --body "Superseded by a new release." + gh pr close "$pr" --delete-branch || true + done + + - name: Create release PR + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ steps.version.outputs.version }} + TAG_PREFIX: ${{ steps.version.outputs.tag_prefix }} + INPUT_BRANCH: ${{ inputs.branch }} + INPUT_PACKAGE: ${{ inputs.package }} + run: | + branch="release/${TAG_PREFIX}-v${VERSION}" + + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b "$branch" + git add -A + git commit -m "${TAG_PREFIX}-v${VERSION}" + git push --force origin "$branch" + + gh pr create \ + --base "$INPUT_BRANCH" \ + --head "$branch" \ + --title "${INPUT_PACKAGE} v${VERSION}" \ + --body "Merging this PR will publish **${INPUT_PACKAGE}** v${VERSION} to PyPI." \ + + + # ── Step 2: Publish on merge ────────────────────────────────── + detect: + name: Detect package + if: | + github.event_name == 'pull_request' + && github.event.pull_request.merged == true + && startsWith(github.event.pull_request.head.ref, 'release/') + runs-on: ubuntu-latest + outputs: + package: ${{ steps.detect.outputs.package }} + tag: ${{ steps.detect.outputs.tag }} + steps: + - name: Parse release branch + id: detect + env: + HEAD_REF: ${{ github.event.pull_request.head.ref }} + run: | + # branch is like release/rtc-v1.2.0, release/api-v1.1.1, release/protocol-v1.1.5 + ref="${HEAD_REF#release/}" + prefix="${ref%%-v*}" + + case "$prefix" in + rtc) package="livekit" ;; + api) package="livekit-api" ;; + protocol) package="livekit-protocol" ;; + *) echo "::error::Unknown release prefix: $prefix"; exit 1 ;; + esac + + echo "package=$package" >> "$GITHUB_OUTPUT" + echo "tag=$ref" >> "$GITHUB_OUTPUT" + echo "Releasing $package (tag: $ref)" + + tag: + name: Tag release + needs: detect + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + ref: ${{ github.event.pull_request.merge_commit_sha }} + + - name: Create git tag + env: + TAG: ${{ needs.detect.outputs.tag }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "$TAG" + git push origin "$TAG" + + # ── RTC builds (multi-platform) ────────────────────────────── + build-rtc: + name: Build RTC wheels (${{ matrix.archs }}) + needs: detect + if: needs.detect.outputs.package == 'livekit' + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + archs: x86_64 + - os: namespace-profile-default-arm64 + archs: aarch64 + - os: windows-latest + archs: AMD64 + - os: macos-latest + archs: x86_64 arm64 + defaults: + run: + working-directory: ./livekit-rtc + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + id: setup-python + with: + python-version: "3.11" + + - name: Build wheels + run: pipx run --python '${{ steps.setup-python.outputs.python-path }}' cibuildwheel==3.3.1 --output-dir dist + env: + CIBW_ARCHS: ${{ matrix.archs }} + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: dist-rtc-${{ matrix.os }} + path: livekit-rtc/dist/*.whl + + build-rtc-sdist: + name: Build RTC sdist + needs: detect + if: needs.detect.outputs.package == 'livekit' + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./livekit-rtc + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + + - name: Build sdist + run: | + pip3 install build + python3 -m build --sdist + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: dist-rtc-sdist + path: livekit-rtc/dist/*.tar.gz + + publish-rtc: + name: Publish livekit (RTC) + needs: [detect, build-rtc, build-rtc-sdist] + if: needs.detect.outputs.package == 'livekit' + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + steps: + - name: Download build artifacts + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + pattern: dist-rtc-* + path: dist + merge-multiple: true + + - name: List distributions + run: ls -la dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 + + # ── API build ──────────────────────────────────────────────── + build-api: + name: Build API + needs: detect + if: needs.detect.outputs.package == 'livekit-api' + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./livekit-api + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + + - name: Build wheel & sdist + run: | + pip3 install build wheel + python3 -m build --wheel --sdist + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: dist-api + path: | + livekit-api/dist/*.whl + livekit-api/dist/*.tar.gz + + publish-api: + name: Publish livekit-api + needs: [detect, build-api] + if: needs.detect.outputs.package == 'livekit-api' + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + steps: + - name: Download build artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: dist-api + path: dist/ + + - name: List distributions + run: ls -la dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 + + # ── Protocol build ─────────────────────────────────────────── + build-protocol: + name: Build Protocol + needs: detect + if: needs.detect.outputs.package == 'livekit-protocol' + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./livekit-protocol + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + + - name: Build wheel & sdist + run: | + pip3 install build wheel + python3 -m build --wheel --sdist + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 + with: + name: dist-protocol + path: | + livekit-protocol/dist/*.whl + livekit-protocol/dist/*.tar.gz + + publish-protocol: + name: Publish livekit-protocol + needs: [detect, build-protocol] + if: needs.detect.outputs.package == 'livekit-protocol' + runs-on: ubuntu-latest + environment: pypi + permissions: + id-token: write + steps: + - name: Download build artifact + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: dist-protocol + path: dist/ + + - name: List distributions + run: ls -la dist/ + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 + + # ── Docs ───────────────────────────────────────────────────── + docs-rtc: + name: Build RTC docs + needs: [detect, publish-rtc] + if: needs.detect.outputs.package == 'livekit' + uses: ./.github/workflows/build-docs.yml + with: + package_dir: livekit-rtc + package_name: livekit.rtc + secrets: inherit + + docs-api: + name: Build API docs + needs: [detect, publish-api] + if: needs.detect.outputs.package == 'livekit-api' + uses: ./.github/workflows/build-docs.yml + with: + package_dir: livekit-api + package_name: livekit.api + secrets: inherit + + docs-protocol: + name: Build Protocol docs + needs: [detect, publish-protocol] + if: needs.detect.outputs.package == 'livekit-protocol' + uses: ./.github/workflows/build-docs.yml + with: + package_dir: livekit-protocol + package_name: livekit.protocol + secrets: inherit diff --git a/.github/workflows/release-gate.yml b/.github/workflows/release-gate.yml new file mode 100644 index 00000000..7ed81085 --- /dev/null +++ b/.github/workflows/release-gate.yml @@ -0,0 +1,41 @@ +name: Release gate + +on: + pull_request: + types: [opened, synchronize, reopened] + pull_request_review: + types: [submitted, dismissed] + +permissions: + pull-requests: read + +jobs: + release-gate: + name: Release gate + runs-on: ubuntu-latest + steps: + - name: Check release PR requirements + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_REF: ${{ github.event.pull_request.head.ref }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_NUMBER: ${{ github.event.pull_request.number }} + REPO: ${{ github.repository }} + run: | + if [[ "$HEAD_REF" != release/* ]]; then + echo "Not a release PR, skipping" + exit 0 + fi + + if [ "$PR_AUTHOR" != "github-actions[bot]" ]; then + echo "::error::Release PRs must be created by the publish workflow, not by '$PR_AUTHOR'" + exit 1 + fi + + approvals=$(gh api "repos/$REPO/pulls/$PR_NUMBER/reviews" \ + --jq '[group_by(.user.login)[] | sort_by(.submitted_at) | last | select(.state == "APPROVED") | .user.login] | length') + echo "Approvals: $approvals" + if [ "$approvals" -lt 2 ]; then + echo "::error::Release PRs require at least 2 approvals (got $approvals)" + exit 1 + fi diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 06514b44..a98a1a4c 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -4,19 +4,24 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 with: python-version: "3.9" - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install ruff + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Install the project + run: uv sync --all-extras --dev + # Use the ruff version pinned in uv.lock rather than uvx's latest release, + # so lint results only change when the lockfile is intentionally updated. - name: Ruff livekit-api - run: ruff check --output-format=github . + run: uv run ruff check --output-format=github . - name: Check format - run: ruff format --check . - + run: uv run ruff format --check . diff --git a/.github/workflows/test-api.yml b/.github/workflows/test-api.yml new file mode 100644 index 00000000..3d14c087 --- /dev/null +++ b/.github/workflows/test-api.yml @@ -0,0 +1,67 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +name: Test API + +permissions: + contents: read + +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + failover: + runs-on: ubuntu-latest + services: + mock-server: + image: livekit/test-server:latest + ports: + - 9999:9999 + - 10000:10000 + - 10001:10001 + - 10002:10002 + steps: + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 + with: + submodules: true + + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: "3.12" + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + + - name: Install livekit-api (without livekit-rtc) + run: | + uv venv .failover-venv + source .failover-venv/bin/activate + uv pip install ./livekit-protocol ./livekit-api pytest aiohttp + + - name: Wait for mock server + run: | + for i in $(seq 1 30); do + curl -sf http://127.0.0.1:9999/settings/regions >/dev/null && exit 0 + sleep 1 + done + echo "mock server did not become ready" && exit 1 + + - name: Run API tests + run: | + source .failover-venv/bin/activate + pytest tests/api -v diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7f6ab5ef..3b0277bb 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,27 +1,141 @@ name: Tests on: - push: - branches: - - main - pull_request: - branches: - - main - workflow_dispatch: + workflow_call: + inputs: + os: + description: "Runner OS (e.g., ubuntu-latest, macos-latest, windows-latest)" + required: true + type: string + python-version: + description: "Python version to test" + required: true + type: string + artifact-name: + description: "Name of the wheel artifact to download" + required: true + type: string + run-id: + description: "Workflow run ID to download artifacts from (optional, uses current run if not specified)" + required: false + type: string + secrets: + LIVEKIT_URL: + required: true + LIVEKIT_API_KEY: + required: true + LIVEKIT_API_SECRET: + required: true jobs: - tests: - name: Run tests - runs-on: ubuntu-latest + test: + name: Test (${{ inputs.os }}, Python ${{ inputs.python-version }}) + runs-on: ${{ inputs.os }} steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7 with: submodules: true lfs: true - - uses: actions/setup-python@v4 - - name: Run tests + - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7 + with: + python-version: ${{ inputs.python-version }} + allow-prereleases: true + + - name: Install uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + with: + enable-cache: true + cache-dependency-glob: "uv.lock" + + - name: Download livekit-rtc wheel (current run) + if: ${{ inputs.run-id == '' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: ${{ inputs.artifact-name }} + path: rtc-wheel + + - name: Download livekit-rtc wheel (from specific run) + if: ${{ inputs.run-id != '' }} + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8 + with: + name: ${{ inputs.artifact-name }} + path: rtc-wheel + run-id: ${{ inputs.run-id }} + github-token: ${{ github.token }} + + - name: Select compatible wheel (macOS) + if: runner.os == 'macOS' + id: select-wheel-macos run: | - python3 ./livekit-rtc/rust-sdks/download_ffi.py --output livekit-rtc/livekit/rtc/resources - pip3 install pytest ./livekit-protocol ./livekit-api ./livekit-rtc - pytest . --ignore=livekit-rtc/rust-sdks + # macOS artifacts contain both x86_64 and arm64 wheels, select the right one + WHEEL=$(python3 -c " + import glob + import platform + import sys + + wheels = glob.glob('rtc-wheel/*.whl') + machine = platform.machine().lower() + + arch_map = { + 'x86_64': ['x86_64'], + 'arm64': ['arm64'], + } + patterns = arch_map.get(machine, [machine]) + + for wheel in wheels: + wheel_lower = wheel.lower() + if any(p in wheel_lower for p in patterns): + print(wheel) + sys.exit(0) + + print(f'No matching wheel found for {machine}', file=sys.stderr) + sys.exit(1) + ") + echo "wheel=$WHEEL" >> $GITHUB_OUTPUT + + - name: Create venv and install dependencies (Unix) + if: runner.os == 'Linux' + run: | + uv venv .test-venv + source .test-venv/bin/activate + uv pip install rtc-wheel/*.whl ./livekit-api ./livekit-protocol + uv pip install pytest pytest-asyncio numpy matplotlib + + - name: Create venv and install dependencies (macOS) + if: runner.os == 'macOS' + run: | + uv venv .test-venv + source .test-venv/bin/activate + uv pip install "${{ steps.select-wheel-macos.outputs.wheel }}" + uv pip install ./livekit-api ./livekit-protocol + uv pip install pytest pytest-asyncio numpy matplotlib + + - name: Create venv and install dependencies (Windows) + if: runner.os == 'Windows' + run: | + uv venv .test-venv + $wheel = (Get-ChildItem rtc-wheel\*.whl)[0].FullName + uv pip install --python .test-venv $wheel .\livekit-api .\livekit-protocol + uv pip install --python .test-venv pytest pytest-asyncio numpy matplotlib + shell: pwsh + + - name: Run tests (Unix) + if: runner.os != 'Windows' + env: + LIVEKIT_URL: ${{ secrets.LIVEKIT_URL }} + LIVEKIT_API_KEY: ${{ secrets.LIVEKIT_API_KEY }} + LIVEKIT_API_SECRET: ${{ secrets.LIVEKIT_API_SECRET }} + run: | + source .test-venv/bin/activate + pytest tests/ livekit-rtc/tests/ + + - name: Run tests (Windows) + if: runner.os == 'Windows' + env: + LIVEKIT_URL: ${{ secrets.LIVEKIT_URL }} + LIVEKIT_API_KEY: ${{ secrets.LIVEKIT_API_KEY }} + LIVEKIT_API_SECRET: ${{ secrets.LIVEKIT_API_SECRET }} + run: .test-venv\Scripts\python.exe -m pytest tests/ livekit-rtc/tests/ + shell: pwsh + \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1756b987..ea0eda1f 100644 --- a/.gitignore +++ b/.gitignore @@ -162,4 +162,6 @@ cython_debug/ # vscode project settings .vscode -.DS_Store \ No newline at end of file +.DS_Store + +docs \ No newline at end of file diff --git a/README.md b/README.md index 5f4bbb3f..cf68bce5 100644 --- a/README.md +++ b/README.md @@ -6,130 +6,356 @@ The LiveKit icon, the name of the repository and some sample code in the background. - - -[![pypi-v](https://img.shields.io/pypi/v/livekit.svg)](https://pypi.org/project/livekit/) - -# 📹🎙️🐍 Python SDK for LiveKit - + + +[![pypi-v](https://img.shields.io/pypi/v/livekit.svg?label=livekit)](https://pypi.org/project/livekit/) +[![pypi-v](https://img.shields.io/pypi/v/livekit-api.svg?label=livekit-api)](https://pypi.org/project/livekit-api/) + +# 📹🎙️🐍 Python SDK for LiveKit + -Use this SDK to add real-time video, audio and data features to your Python app. By connecting to a self- or cloud-hosted LiveKit server, you can quickly build applications like interactive live streaming or video calls with just a few lines of code. - - -This repo contains two packages - -- [livekit](https://pypi.org/project/livekit/): Real-time SDK for connecting to LiveKit as a participant -- [livekit-api](https://pypi.org/project/livekit-api/): Access token generation and server APIs - -## Using Server API - -```shell -$ pip install livekit-api -``` - -### Generating an access token - -```python -from livekit import api -import os - -# will automatically use the LIVEKIT_API_KEY and LIVEKIT_API_SECRET env vars -token = api.AccessToken() \ - .with_identity("python-bot") \ - .with_name("Python Bot") \ - .with_grants(api.VideoGrants( - room_join=True, - room="my-room", - )).to_jwt() -``` - -### Creating a room - -RoomService uses asyncio and aiohttp to make API calls. It needs to be used with an event loop. - -```python -from livekit import api -import asyncio - -async def main(): - lkapi = api.LiveKitAPI( - 'http://localhost:7880', - ) - room_info = await lkapi.room.create_room( - api.CreateRoomRequest(name="my-room"), - ) - print(room_info) - results = await lkapi.room.list_rooms(api.ListRoomsRequest()) - print(results) - await lkapi.aclose() - -asyncio.get_event_loop().run_until_complete(main()) -``` - -## Using Real-time SDK - -```shell -$ pip install livekit -``` - -### Connecting to a room - -```python -from livekit import rtc - -async def main(): - room = rtc.Room() - - @room.on("participant_connected") - def on_participant_connected(participant: rtc.RemoteParticipant): - logging.info( - "participant connected: %s %s", participant.sid, participant.identity) - - async def receive_frames(stream: rtc.VideoStream): - async for frame in video_stream: - # received a video frame from the track, process it here - pass - - # track_subscribed is emitted whenever the local participant is subscribed to a new track - @room.on("track_subscribed") - def on_track_subscribed(track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant): - logging.info("track subscribed: %s", publication.sid) - if track.kind == rtc.TrackKind.KIND_VIDEO: - video_stream = rtc.VideoStream(track) - asyncio.ensure_future(receive_frames(video_stream)) - - # By default, autosubscribe is enabled. The participant will be subscribed to - # all published tracks in the room - await room.connect(URL, TOKEN) - logging.info("connected to room %s", room.name) - - # participants and tracks that are already available in the room - # participant_connected and track_published events will *not* be emitted for them - for participant in room.participants.items(): - for publication in participant.tracks.items(): - print("track publication: %s", publication.sid) -``` - -## Examples - -- [Facelandmark](https://github.com/livekit/python-sdks/tree/main/examples/face_landmark): Use mediapipe to detect face landmarks (eyes, nose ...) -- [Basic room](https://github.com/livekit/python-sdks/blob/main/examples/basic_room.py): Connect to a room -- [Publish hue](https://github.com/livekit/python-sdks/blob/main/examples/publish_hue.py): Publish a rainbow video track -- [Publish wave](https://github.com/livekit/python-sdks/blob/main/examples/publish_hue.py): Publish a sine wave - -## Getting help / Contributing - -Please join us on [Slack](https://livekit.io/join-slack) to get help from our devs / community members. We welcome your contributions(PRs) and details can be discussed there. - +Use this SDK to add realtime video, audio and data features to your Python app. By connecting to LiveKit Cloud or a self-hosted server, you can quickly build applications such as multi-modal AI, live streaming, or video calls with just a few lines of code. + + +This repo contains two packages + +- [livekit](https://pypi.org/project/livekit/): Real-time SDK for connecting to LiveKit as a participant +- [livekit-api](https://pypi.org/project/livekit-api/): Access token generation and server APIs + +## Using Server API + +```shell +$ pip install livekit-api +``` + +### Generating an access token + +```python +from livekit import api +import os + +# will automatically use the LIVEKIT_API_KEY and LIVEKIT_API_SECRET env vars +token = api.AccessToken() \ + .with_identity("python-bot") \ + .with_name("Python Bot") \ + .with_grants(api.VideoGrants( + room_join=True, + room="my-room", + )).to_jwt() +``` + +### Creating a room + +RoomService uses asyncio and aiohttp to make API calls. It needs to be used with an event loop. + +```python +from livekit import api +import asyncio + +async def main(): + lkapi = api.LiveKitAPI("https://my-project.livekit.cloud") + room_info = await lkapi.room.create_room( + api.CreateRoomRequest(name="my-room"), + ) + print(room_info) + results = await lkapi.room.list_rooms(api.ListRoomsRequest()) + print(results) + await lkapi.aclose() + +asyncio.run(main()) +``` + +### Authentication + +Authenticate with an API key and secret (recommended for backend use), or with a +pre-signed token for client-side use, where the API secret must not be exposed. +Any omitted value falls back to `LIVEKIT_URL`, `LIVEKIT_API_KEY`, +`LIVEKIT_API_SECRET`, and `LIVEKIT_TOKEN`. + +```python +# API key & secret (backend) +lkapi = api.LiveKitAPI("https://my-project.livekit.cloud", api_key="...", api_secret="...") + +# pre-signed token (client-side); the token must already carry the grants for the calls +lkapi = api.LiveKitAPI.with_token(my_token, "https://my-project.livekit.cloud") +``` + +### Using other APIs + +Services can be accessed via the LiveKitAPI object. + +```python +lkapi = api.LiveKitAPI("https://my-project.livekit.cloud") + +# Room Service +room_svc = lkapi.room + +# Egress Service +egress_svc = lkapi.egress + +# Ingress Service +ingress_svc = lkapi.ingress + +# Sip Service +sip_svc = lkapi.sip + +# Agent Dispatch +dispatch_svc = lkapi.agent_dispatch + +# Connector Service +connector_svc = lkapi.connector +``` + +### Error handling + +A failed server API call raises `api.ServerError`, which exposes the error +`code`, `message`, and any server-provided `metadata`. + +```python +try: + await lkapi.room.create_room(api.CreateRoomRequest(name="my-room")) +except api.ServerError as e: + print(e.code, e.message) +``` + +A failed SIP dial (e.g. the callee is busy or doesn't answer) raises +`api.SipCallError`, a `ServerError` subclass that also exposes the SIP response +status: + +```python +try: + await lkapi.sip.create_sip_participant(api.CreateSIPParticipantRequest( + sip_trunk_id="ST_...", + sip_call_to="+15105550100", + room_name="my-room", + wait_until_answered=True, + )) +except api.SipCallError as e: + print(e) # e.g. "SIP call failed: 486 Busy Here (resource_exhausted)" + if e.sip_status_code == 486: + ... # busy +except api.ServerError as e: + print(e.code, e.message) # any other API error +``` + +## Using Real-time SDK + +```shell +$ pip install livekit +``` + +### Connecting to a room + +see [room_example](examples/room_example.py) for full example + +```python +from livekit import rtc + +async def main(): + room = rtc.Room() + + @room.on("participant_connected") + def on_participant_connected(participant: rtc.RemoteParticipant): + logging.info( + "participant connected: %s %s", participant.sid, participant.identity) + + async def receive_frames(stream: rtc.VideoStream): + async for frame in stream: + # received a video frame from the track, process it here + pass + + # track_subscribed is emitted whenever the local participant is subscribed to a new track + @room.on("track_subscribed") + def on_track_subscribed(track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant): + logging.info("track subscribed: %s", publication.sid) + if track.kind == rtc.TrackKind.KIND_VIDEO: + video_stream = rtc.VideoStream(track) + asyncio.ensure_future(receive_frames(video_stream)) + + # By default, autosubscribe is enabled. The participant will be subscribed to + # all published tracks in the room + await room.connect(URL, TOKEN) + logging.info("connected to room %s", room.name) + + # participants and tracks that are already available in the room + # participant_connected and track_published events will *not* be emitted for them + for identity, participant in room.remote_participants.items(): + print(f"identity: {identity}") + print(f"participant: {participant}") + for tid, publication in participant.track_publications.items(): + print(f"\ttrack id: {publication}") +``` + +### RPC + +Perform your own predefined method calls from one participant to another. + +This feature is especially powerful when used with [Agents](https://docs.livekit.io/agents), for instance to forward LLM function calls to your client application. + +#### Registering an RPC method + +The participant who implements the method and will receive its calls must first register support: + +```python +@room.local_participant.register_rpc_method("greet") +async def handle_greet(data: RpcInvocationData): + print(f"Received greeting from {data.caller_identity}: {data.payload}") + return f"Hello, {data.caller_identity}!" +``` + +In addition to the payload, your handler will also receive `response_timeout`, which informs you the maximum time available to return a response. If you are unable to respond in time, the call will result in an error on the caller's side. + +#### Performing an RPC request + +The caller may then initiate an RPC call like so: + +```python +try: + response = await room.local_participant.perform_rpc( + destination_identity='recipient-identity', + method='greet', + payload='Hello from RPC!' + ) + print(f"RPC response: {response}") +except Exception as e: + print(f"RPC call failed: {e}") +``` + +You may find it useful to adjust the `response_timeout` parameter, which indicates the amount of time you will wait for a response. We recommend keeping this value as low as possible while still satisfying the constraints of your application. + +## Using local media devices + +The `MediaDevices` class provides a high-level interface for working with local audio input (microphone) and output (speakers) devices. It's built on top of the `sounddevice` library and integrates seamlessly with LiveKit's audio processing features. In order to use `MediaDevices`, you must have the `sounddevice` library installed in your local Python environment, if it's not available, `MediaDevices` will not work. + +### Capturing microphone input + +```python +from livekit import rtc + +# Create a MediaDevices instance +devices = rtc.MediaDevices() + +# Open the default microphone with audio processing enabled +mic = devices.open_input( + enable_aec=True, # Acoustic Echo Cancellation + noise_suppression=True, # Noise suppression + high_pass_filter=True, # High-pass filter + auto_gain_control=True # Automatic gain control +) + +# Use the audio source to create a track and publish it +track = rtc.LocalAudioTrack.create_audio_track("microphone", mic.source) +await room.local_participant.publish_track(track) + +# Clean up when done +await mic.aclose() +``` + +### Playing audio to speakers + +```python +# Open the default output device +player = devices.open_output() + +# Add remote audio tracks to the player (typically in a track_subscribed handler) +@room.on("track_subscribed") +def on_track_subscribed(track: rtc.Track, publication, participant): + if track.kind == rtc.TrackKind.KIND_AUDIO: + player.add_track(track) + +# Start playback (mixes all added tracks) +await player.start() + +# Clean up when done +await player.aclose() +``` + +### Full duplex audio (microphone + speakers) + +For full duplex audio with echo cancellation, open the input device first (with AEC enabled), then open the output device. The output player will automatically feed the APM's reverse stream for effective echo cancellation: + +```python +devices = rtc.MediaDevices() + +# Open microphone with AEC +mic = devices.open_input(enable_aec=True) + +# Open speakers - automatically uses the mic's APM for echo cancellation +player = devices.open_output() + +# Publish microphone +track = rtc.LocalAudioTrack.create_audio_track("mic", mic.source) +await room.local_participant.publish_track(track) + +# Add remote tracks and start playback +player.add_track(remote_audio_track) +await player.start() +``` + +### Listing available devices + +```python +devices = rtc.MediaDevices() + +# List input devices +input_devices = devices.list_input_devices() +for device in input_devices: + print(f"{device['index']}: {device['name']}") + +# List output devices +output_devices = devices.list_output_devices() +for device in output_devices: + print(f"{device['index']}: {device['name']}") + +# Get default device indices +default_input = devices.default_input_device() +default_output = devices.default_output_device() +``` + +See [publish_mic.py](examples/local_audio/publish_mic.py) and [full_duplex.py](examples/local_audio/full_duplex.py) for complete examples. + + +#### Errors + +LiveKit is a dynamic realtime environment and calls can fail for various reasons. + +You may throw errors of the type `RpcError` with a string `message` in an RPC method handler and they will be received on the caller's side with the message intact. Other errors will not be transmitted and will instead arrive to the caller as `1500` ("Application Error"). Other built-in errors are detailed in `RpcError`. + +## Hardware video codec support + +The underlying Rust SDK ships with platform-specific hardware-accelerated encoders/decoders, These are used automatically when available and compatible with the runtime environment (OS, drivers, GPU, and codec). + +| Platform | Codec(s) | Encoder | Decoder | Backend | +| ------------------------------ | ---------- | ------- | ------- | -------------------------------------- | +| macOS | H264, H265 | ✓ | ✓ | VideoToolbox | +| Linux (AMD GPU) | H264 | ✓ | | VAAPI | +| Linux x64 (NVIDIA GPU) | H264, H265 | ✓ | ✓ | NVENC / NVDEC (NVIDIA Video Codec SDK) | + +Software encoders (libvpx for VP8/VP9, libaom for AV1, OpenH264 for H264) are used as a fallback when hardware acceleration is not available. + +> **Note:** Availability depends on the specific machine configuration, including GPU model, driver support, and runtime environment. + +## Examples + +- [Facelandmark](https://github.com/livekit/python-sdks/tree/main/examples/face_landmark): Use mediapipe to detect face landmarks (eyes, nose ...) +- [Basic room](https://github.com/livekit/python-sdks/blob/main/examples/basic_room.py): Connect to a room +- [Publish hue](https://github.com/livekit/python-sdks/blob/main/examples/publish_hue.py): Publish a rainbow video track +- [Publish wave](https://github.com/livekit/python-sdks/blob/main/examples/publish_wave.py): Publish a sine wave + +## Getting help / Contributing + +Please join us on [Slack](https://livekit.io/join-slack) to get help from our devs / community members. We welcome your contributions(PRs) and details can be discussed there. +
- - - - - + + + + + +
LiveKit Ecosystem
Real-time SDKsReact Components · Browser · iOS/macOS · Android · Flutter · React Native · Rust · Node.js · Python · Unity (web) · Unity (beta)
Server APIsNode.js · Golang · Ruby · Java/Kotlin · Python · Rust · PHP (community)
Agents FrameworksPython · Playground
ServicesLivekit server · Egress · Ingress · SIP
ResourcesDocs · Example apps · Cloud · Self-hosting · CLI
LiveKit SDKsBrowser · iOS/macOS/visionOS · Android · Flutter · React Native · Rust · Node.js · Python · Unity · Unity (WebGL) · ESP32
Server APIsNode.js · Golang · Ruby · Java/Kotlin · Python · Rust · PHP (community) · .NET (community)
UI ComponentsReact · Android Compose · SwiftUI · Flutter
Agents FrameworksPython · Node.js · Playground
ServicesLiveKit server · Egress · Ingress · SIP
ResourcesDocs · Example apps · Cloud · Self-hosting · CLI
- + diff --git a/dev-requirements.txt b/dev-requirements.txt deleted file mode 100644 index a31ca5fd..00000000 --- a/dev-requirements.txt +++ /dev/null @@ -1,11 +0,0 @@ -# download binaries -requests - -# build wheels -wheel -setuptools -twine -auditwheel; sys_platform == 'linux' -cibuildwheel - -pytest diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000..c4ea88e3 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,301 @@ +# LiveKit Python SDK Examples + +This directory contains examples demonstrating various features of the LiveKit Python SDK. + +## Prerequisites + +Set the following environment variables: + +```bash +export LIVEKIT_URL=ws://localhost:7880 +export LIVEKIT_API_KEY=devkey +export LIVEKIT_API_SECRET=secret +``` + +## Examples Overview + +| Example | Description | +|---------|-------------| +| [basic_room.py](#basic_roompy) | Room connection with PlatformAudio and synthetic audio modes | +| [room_example.py](#room_examplepy) | Basic room connection and event handling | +| [api.py](#apipy) | Room management via LiveKit API | +| [e2ee.py](#e2eepy) | End-to-end encryption demo | +| [rpc.py](#rpcpy) | Remote Procedure Call between participants | +| [multiple_connections.py](#multiple_connectionspy) | Sequential connections for sync frameworks | +| [participant_attributes.py](#participant_attributespy) | Dynamic participant attributes | +| [publish_wave.py](#publish_wavepy) | Publish sine wave audio | +| [publish_hue.py](#publish_huepy) | Publish color-cycling video | +| [play_audio_stream.py](#play_audio_streampy) | Play received audio with sounddevice | +| [webhook.py](#webhookpy) | Webhook event handling | +| [agent_dispatch.py](#agent_dispatchpy) | Manual agent dispatch | + +### Subdirectories + +| Directory | Description | +|-----------|-------------| +| [face_landmark/](face_landmark/) | Facial landmark detection using MediaPipe | +| [video-stream/](video-stream/) | Video/audio sync with AVSynchronizer | +| [data_tracks/](data_tracks/) | Data channel examples | +| [data-streams/](data-streams/) | Data streaming examples | +| [local_audio/](local_audio/) | Local audio capture with dB metering | + +--- + +## basic_room.py + +The most comprehensive example, demonstrating room connection with audio capabilities. It showcases two audio capture modes that can be used independently or combined. + +### Quick Start + +```bash +# List available audio devices +python basic_room.py --list-devices + +# Connect with PlatformAudio (recommended) +python basic_room.py --platform-audio --room my-room + +# Connect with specific devices +python basic_room.py --platform-audio --mic-id "device-guid" --speaker-id "device-guid" + +# Publish a WAV file +python basic_room.py --file audio.wav --room my-room + +# Mix microphone + WAV file (both modes together) +python basic_room.py --platform-audio --file background.wav --room my-room +``` + +### Audio Modes + +#### PlatformAudio Mode (`--platform-audio`) + +Uses WebRTC's Audio Device Module (ADM) for microphone capture. **Recommended for most applications.** + +**Features:** +- Built-in voice processing: + - **Echo Cancellation (AEC)** - removes echo from speaker playback + - **Noise Suppression (NS)** - reduces background noise + - **Auto Gain Control (AGC)** - normalizes audio levels +- Hardware-accelerated processing on supported platforms (e.g., iOS VPIO) +- Automatic speaker playout for received audio +- Device enumeration and selection +- No external audio libraries required + +**Limitations:** +- No direct access to raw audio frames (ADM sends directly to WebRTC) +- Cannot apply custom audio processing before publishing + +```python +platform_audio = rtc.PlatformAudio() +source = platform_audio.create_audio_source( + rtc.PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + ) +) +track = rtc.LocalAudioTrack.create_audio_track("microphone", source) +``` + +#### Synthetic Mode (Default) + +Manual control over audio frames via `AudioSource.capture_frame()`. Use this for custom audio processing or programmatic audio generation. + +**Features:** +- Full control over audio data +- Access to raw audio frames for custom processing +- Generate synthetic audio (files, TTS, audio synthesis) +- Apply custom filters, effects, or ML models + +**Limitations:** +- No built-in AEC/NS/AGC - implement yourself or use `AudioProcessingModule` +- Must handle speaker playout manually via `AudioStream` +- Requires external audio libraries for mic capture (sounddevice, pyaudio) + +```python +source = rtc.AudioSource(sample_rate=48000, num_channels=1) +track = rtc.LocalAudioTrack.create_audio_track("audio", source) + +frame = rtc.AudioFrame(data=audio_bytes, sample_rate=48000, ...) +await source.capture_frame(frame) +``` + +#### Mixing Both Modes + +PlatformAudio and Synthetic modes can run simultaneously. The `--file` option demonstrates this by publishing a WAV file (synthetic) alongside microphone capture (PlatformAudio). + +```bash +python basic_room.py --platform-audio --file music.wav --room my-room +``` + +This creates two audio tracks: +1. **Microphone** - via PlatformAudio with voice processing +2. **File** - via synthetic mode from WAV + +Use cases: background music while speaking, sound effects, mixing pre-recorded audio with live input. + +### Command Line Options + +| Option | Description | +|--------|-------------| +| `--list-devices` | List available audio devices and exit | +| `--platform-audio` | Use PlatformAudio for microphone (recommended) | +| `--file WAV_PATH` | Publish audio from WAV file (synthetic mode) | +| `--room NAME` | Room name (default: my-room) | +| `--mic-id ID` | Select microphone by device ID | +| `--speaker-id ID` | Select speaker by device ID | + +### When to Use Each Mode + +| Use Case | Mode | +|----------|------| +| Voice/video calls | PlatformAudio | +| Conferencing apps | PlatformAudio | +| Playing audio files | Synthetic | +| Text-to-speech | Synthetic | +| Custom audio processing | Synthetic | +| ML audio effects | Synthetic | +| Background music + voice | Both | + +--- + +## local_audio/ + +Examples for local audio capture with microphone and speaker management. + +### full_duplex.py + +Full-duplex audio with microphone capture and speaker playout. + +```bash +cd local_audio +python full_duplex.py +``` + +Features: +- Microphone capture via MediaDevices +- Speaker playout for received audio +- Real-time dB level metering + +### publish_mic.py + +Publish microphone audio with dB level monitoring. + +```bash +cd local_audio +python publish_mic.py +``` + +Features: +- Microphone capture with AEC enabled +- Real-time dB level visualization + +--- + +## room_example.py + +Basic room connection demonstrating event handling and participant tracking. + +```bash +python room_example.py +``` + +--- + +## api.py + +Room management using the LiveKit API (create rooms, list rooms). + +```bash +python api.py +``` + +--- + +## e2ee.py + +End-to-end encryption with a rotating 3D cube visualization. + +```bash +python e2ee.py +``` + +--- + +## rpc.py + +Remote Procedure Call (RPC) between participants - greetings, math operations, timeout handling. + +```bash +python rpc.py +``` + +--- + +## multiple_connections.py + +Sequential room connections in a single thread. Useful for Django/Flask integration. + +```bash +python multiple_connections.py +``` + +--- + +## participant_attributes.py + +Set, update, and delete participant attributes dynamically. + +```bash +python participant_attributes.py +``` + +--- + +## publish_wave.py + +Publish a sine wave audio track at a specified frequency. + +```bash +python publish_wave.py +``` + +--- + +## publish_hue.py + +Publish a color-cycling animated video track. + +```bash +python publish_hue.py +``` + +--- + +## play_audio_stream.py + +Play incoming audio from remote participants using sounddevice. + +```bash +pip install sounddevice +python play_audio_stream.py +``` + +--- + +## webhook.py + +Handle LiveKit webhook events using aiohttp. + +```bash +python webhook.py +``` + +--- + +## agent_dispatch.py + +Manually dispatch agents to rooms (instead of automatic dispatch). + +```bash +python agent_dispatch.py +``` diff --git a/examples/agent_dispatch.py b/examples/agent_dispatch.py new file mode 100644 index 00000000..cbbadf49 --- /dev/null +++ b/examples/agent_dispatch.py @@ -0,0 +1,76 @@ +import asyncio +from livekit import api + +room_name = "my-room" +agent_name = "test-agent" + +""" +This example demonstrates how to have an agent join a room +without using the automatic dispatch. In order to use this +feature, you must have an agent running with `agent_name` set +when defining your WorkerOptions. A dispatch requests the +agent to enter a specific room with optional metadata. + +You can also specify a `deployment` to target a specific agent deployment. +Leave empty to target the production deployment. +""" + + +async def create_explicit_dispatch(): + lkapi = api.LiveKitAPI() + + dispatch = await lkapi.agent_dispatch.create_dispatch( + api.CreateAgentDispatchRequest( + agent_name=agent_name, + room=room_name, + metadata="my_metadata", + # deployment="staging", # Optional: target a specific deployment + ) + ) + print("created dispatch", dispatch) + + dispatches = await lkapi.agent_dispatch.list_dispatch(room_name=room_name) + print(f"there are {len(dispatches)} dispatches in {room_name}") + await lkapi.aclose() + + +""" +When agent name is set, the agent will no longer be automatically dispatched +to new rooms. If you want that agent to be dispatched to a new room as soon as +the participant connects, you can set the RoomConfiguration with the agent +definition in the access token. + +You can also specify a `deployment` in the RoomAgentDispatch to target a specific +agent deployment. Leave empty to target the production deployment. +""" + + +async def create_token_with_agent_dispatch() -> str: + token = ( + api.AccessToken() + .with_identity("my_participant") + .with_grants(api.VideoGrants(room_join=True, room=room_name)) + .with_room_config( + api.RoomConfiguration( + agents=[ + api.RoomAgentDispatch( + agent_name="test-agent", + metadata="my_metadata", + # deployment="staging", # Optional: target a specific deployment + ) + ], + ), + ) + .to_jwt() + ) + return token + + +async def main(): + token = await create_token_with_agent_dispatch() + print("created participant token", token) + print("creating explicit dispatch") + await create_explicit_dispatch() + + +asyncio.run(main()) diff --git a/examples/api.py b/examples/api.py index bdb6348a..f9e34265 100644 --- a/examples/api.py +++ b/examples/api.py @@ -3,8 +3,8 @@ async def main(): - # will automatically use the LIVEKIT_API_KEY and LIVEKIT_API_SECRET env vars - lkapi = api.LiveKitAPI("http://localhost:7880") + # will automatically use LIVEKIT_URL, LIVEKIT_API_KEY and LIVEKIT_API_SECRET env vars + lkapi = api.LiveKitAPI() room_info = await lkapi.room.create_room( api.CreateRoomRequest(name="my-room"), ) @@ -15,4 +15,4 @@ async def main(): if __name__ == "__main__": - asyncio.get_event_loop().run_until_complete(main()) + asyncio.run(main()) diff --git a/examples/basic_room.py b/examples/basic_room.py index 7d1544b5..6d885665 100644 --- a/examples/basic_room.py +++ b/examples/basic_room.py @@ -1,130 +1,334 @@ +""" +LiveKit Basic Room Example + +This example demonstrates connecting to a LiveKit room with audio capabilities. +It supports two audio modes: + +1. PlatformAudio Mode (--platform-audio, RECOMMENDED): + - Uses WebRTC's Audio Device Module (ADM) for microphone capture + - Built-in echo cancellation (AEC), noise suppression (NS), auto gain control (AGC) + - Automatic speaker playout for received audio + - No external audio libraries needed + - Cannot access raw audio frames (ADM sends directly to WebRTC) + +2. Synthetic Mode (default, for advanced use): + - Manual audio frame capture via AudioSource.capture_frame() + - Requires external audio libraries (e.g., sounddevice, pyaudio) + - Full control over audio processing pipeline + - Access to raw audio frames for custom processing + - No built-in AEC/NS/AGC - must implement yourself or use AudioProcessingModule + - Must handle speaker playout manually + +Usage: + # List available audio devices + python basic_room.py --list-devices + + # Connect with PlatformAudio (recommended) + python basic_room.py --platform-audio --room my-room + + # Connect with PlatformAudio and specific devices + python basic_room.py --platform-audio --mic-id "device-guid" --speaker-id "device-guid" + + # Publish a WAV file instead of microphone + python basic_room.py --file audio.wav --room my-room + + # Publish both microphone and WAV file + python basic_room.py --platform-audio --file audio.wav --room my-room + +Environment variables: + LIVEKIT_URL - LiveKit server URL (e.g., ws://localhost:7880) + LIVEKIT_API_KEY - API key for token generation + LIVEKIT_API_SECRET - API secret for token generation +""" + +import argparse import asyncio import logging -from signal import SIGINT, SIGTERM -from typing import Union import os +import struct +import wave +from signal import SIGINT, SIGTERM +from typing import Optional from livekit import api, rtc -# ensure LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are set +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="LiveKit Basic Room Example", + formatter_class=argparse.RawDescriptionHelpFormatter, + epilog=__doc__, + ) -async def main(room: rtc.Room) -> None: - @room.on("participant_connected") - def on_participant_connected(participant: rtc.RemoteParticipant) -> None: - logging.info( - "participant connected: %s %s", participant.sid, participant.identity - ) + parser.add_argument( + "--list-devices", + action="store_true", + help="List available audio devices and exit", + ) - @room.on("participant_disconnected") - def on_participant_disconnected(participant: rtc.RemoteParticipant): - logging.info( - "participant disconnected: %s %s", participant.sid, participant.identity - ) + parser.add_argument( + "--platform-audio", + action="store_true", + help="Use PlatformAudio (WebRTC ADM) for microphone capture. " + "Provides built-in AEC, NS, AGC. Recommended for most use cases.", + ) + + parser.add_argument( + "--file", + type=str, + metavar="WAV_PATH", + help="Publish audio from a WAV file. Can be combined with --platform-audio " + "to publish both microphone and file audio.", + ) + + parser.add_argument( + "--room", + type=str, + default="my-room", + help="Room name to join (default: my-room)", + ) + + parser.add_argument( + "--mic-id", + type=str, + metavar="DEVICE_ID", + help="Select microphone by device ID (from --list-devices). " + "Only used with --platform-audio.", + ) + + parser.add_argument( + "--speaker-id", + type=str, + metavar="DEVICE_ID", + help="Select speaker by device ID (from --list-devices). Only used with --platform-audio.", + ) + + return parser.parse_args() + + +def list_audio_devices() -> None: + """List available audio devices using PlatformAudio.""" + try: + platform_audio = rtc.PlatformAudio() + except rtc.PlatformAudioError as e: + print(f"Failed to initialize PlatformAudio: {e}") + return + + print("\nRecording devices (microphones):") + recording_devices = platform_audio.recording_devices() + if not recording_devices: + print(" (none)") + else: + for device in recording_devices: + print(f" [{device.index}] {device.name}") + print(f" ID: {device.id}") + + print("\nPlayout devices (speakers):") + playout_devices = platform_audio.playout_devices() + if not playout_devices: + print(" (none)") + else: + for device in playout_devices: + print(f" [{device.index}] {device.name}") + print(f" ID: {device.id}") + + print("\nUse --mic-id or --speaker-id with the device ID to select a specific device.") + + +def load_wav_file(path: str) -> tuple[bytes, int, int, int]: + """Load a WAV file and return (samples, sample_rate, channels, bits_per_sample).""" + with wave.open(path, "rb") as wav: + sample_rate = wav.getframerate() + channels = wav.getnchannels() + bits_per_sample = wav.getsampwidth() * 8 + frames = wav.readframes(wav.getnframes()) + + # Convert to 16-bit signed integers if needed + if bits_per_sample == 8: + # Convert unsigned 8-bit to signed 16-bit + samples = struct.unpack(f"{len(frames)}B", frames) + samples = [(s - 128) * 256 for s in samples] + frames = struct.pack(f"{len(samples)}h", *samples) + bits_per_sample = 16 + elif bits_per_sample == 24: + # Convert 24-bit to 16-bit (drop lower 8 bits) + samples = [] + for i in range(0, len(frames), 3): + # 24-bit little-endian: sign-extend to 32-bit, then take upper 16 bits + # If high bit (bit 23) is set, the sample is negative - extend with 0xFF + sign_byte = b"\xff" if frames[i + 2] & 0x80 else b"\x00" + sample = struct.unpack("> 8 + samples.append(sample) + frames = struct.pack(f"{len(samples)}h", *samples) + bits_per_sample = 16 + elif bits_per_sample == 32: + # Convert 32-bit to 16-bit + samples = struct.unpack(f"<{len(frames) // 4}i", frames) + samples = [s >> 16 for s in samples] + frames = struct.pack(f"{len(samples)}h", *samples) + bits_per_sample = 16 + + return frames, sample_rate, channels, bits_per_sample + + +async def publish_wav_file(room: rtc.Room, wav_path: str, running: asyncio.Event) -> None: + """Publish audio from a WAV file using synthetic mode (AudioSource). - @room.on("local_track_published") - def on_local_track_published( - publication: rtc.LocalTrackPublication, - track: Union[rtc.LocalAudioTrack, rtc.LocalVideoTrack], - ): - logging.info("local track published: %s", publication.sid) - - @room.on("active_speakers_changed") - def on_active_speakers_changed(speakers: list[rtc.Participant]): - logging.info("active speakers changed: %s", speakers) - - @room.on("local_track_unpublished") - def on_local_track_unpublished(publication: rtc.LocalTrackPublication): - logging.info("local track unpublished: %s", publication.sid) - - @room.on("track_published") - def on_track_published( - publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): - logging.info( - "track published: %s from participant %s (%s)", - publication.sid, - participant.sid, - participant.identity, + This demonstrates synthetic mode where you manually push audio frames. + Use this approach when you need to: + - Process audio before publishing + - Generate synthetic audio + - Play audio files + """ + logging.info(f"Loading WAV file: {wav_path}") + try: + audio_data, sample_rate, channels, _ = load_wav_file(wav_path) + except Exception as e: + logging.error(f"Failed to load WAV file: {e}") + return + + logging.info(f"WAV file: {sample_rate}Hz, {channels} channels, {len(audio_data)} bytes") + + # Create AudioSource for synthetic mode + # SYNTHETIC MODE LIMITATION: You must manually capture frames in a loop. + # There is no built-in AEC/NS/AGC - audio is sent exactly as provided. + source = rtc.AudioSource( + sample_rate=sample_rate, + num_channels=channels, + queue_size_ms=100, # Small queue for file playback + ) + + track = rtc.LocalAudioTrack.create_audio_track("audio-file", source) + + # Publish the track + publication = await room.local_participant.publish_track( + track, + rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_UNKNOWN), + ) + logging.info(f"Published file audio track: {publication.sid}") + + # Convert audio data to samples + num_samples = len(audio_data) // 2 # 16-bit = 2 bytes per sample + samples = list(struct.unpack(f"<{num_samples}h", audio_data)) + + # Play audio in 10ms frames (standard WebRTC frame size) + samples_per_frame = (sample_rate * channels) // 100 # 10ms worth of samples + position = 0 + frame_count = 0 + + logging.info("Starting WAV playback loop...") + + while running.is_set(): + # Get next frame of samples + end = min(position + samples_per_frame, len(samples)) + frame_samples = samples[position:end] + + # Pad with silence if we don't have enough samples + if len(frame_samples) < samples_per_frame: + frame_samples = frame_samples + [0] * (samples_per_frame - len(frame_samples)) + + # Create AudioFrame + frame = rtc.AudioFrame( + data=bytes(struct.pack(f"<{len(frame_samples)}h", *frame_samples)), + sample_rate=sample_rate, + num_channels=channels, + samples_per_channel=samples_per_frame // channels, ) - @room.on("track_unpublished") - def on_track_unpublished( - publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): - logging.info("track unpublished: %s", publication.sid) + # Capture frame (this queues it for sending) + await source.capture_frame(frame) + + position += samples_per_frame + frame_count += 1 + + # Loop the file + if position >= len(samples): + position = 0 + logging.info("WAV file looping...") + + # Wait ~10ms before next frame + await asyncio.sleep(0.01) + + logging.info(f"WAV playback stopped after {frame_count} frames") + + +async def main(room: rtc.Room, args: argparse.Namespace) -> None: + running = asyncio.Event() + running.set() + + # Setup room event handlers + @room.on("participant_connected") + def on_participant_connected(participant: rtc.RemoteParticipant) -> None: + logging.info(f"Participant connected: {participant.identity} ({participant.sid})") + + @room.on("participant_disconnected") + def on_participant_disconnected(participant: rtc.RemoteParticipant) -> None: + logging.info(f"Participant disconnected: {participant.identity}") @room.on("track_subscribed") def on_track_subscribed( track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant, - ): - logging.info("track subscribed: %s", publication.sid) - if track.kind == rtc.TrackKind.KIND_VIDEO: - _video_stream = rtc.VideoStream(track) - # video_stream is an async iterator that yields VideoFrame - elif track.kind == rtc.TrackKind.KIND_AUDIO: - print("Subscribed to an Audio Track") - _audio_stream = rtc.AudioStream(track) - # audio_stream is an async iterator that yields AudioFrame + ) -> None: + logging.info(f"Track subscribed: {track.kind} from {participant.identity}") + if track.kind == rtc.TrackKind.KIND_AUDIO: + # When using PlatformAudio, received audio is automatically played + # through the selected speaker. No manual handling needed. + # + # For synthetic mode or custom processing, you would create an + # AudioStream and iterate over frames: + # audio_stream = rtc.AudioStream(track) + # async for frame_event in audio_stream: + # process(frame_event.frame) + logging.info("Audio track subscribed - audio will play through speaker") @room.on("track_unsubscribed") def on_track_unsubscribed( track: rtc.Track, publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant, - ): - logging.info("track unsubscribed: %s", publication.sid) - - @room.on("track_muted") - def on_track_muted( - publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): - logging.info("track muted: %s", publication.sid) - - @room.on("track_unmuted") - def on_track_unmuted( - publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant - ): - logging.info("track unmuted: %s", publication.sid) - - @room.on("data_received") - def on_data_received(data: rtc.DataPacket): - logging.info("received data from %s: %s", data.participant.identity, data.data) - - @room.on("connection_quality_changed") - def on_connection_quality_changed( - participant: rtc.Participant, quality: rtc.ConnectionQuality - ): - logging.info("connection quality changed for %s", participant.identity) - - @room.on("track_subscription_failed") - def on_track_subscription_failed( - participant: rtc.RemoteParticipant, track_sid: str, error: str - ): - logging.info("track subscription failed: %s %s", participant.identity, error) + ) -> None: + logging.info(f"Track unsubscribed: {track.kind} from {participant.identity}") @room.on("connection_state_changed") - def on_connection_state_changed(state: rtc.ConnectionState): - logging.info("connection state changed: %s", state) - - @room.on("connected") - def on_connected() -> None: - logging.info("connected") + def on_connection_state_changed(state: rtc.ConnectionState) -> None: + logging.info(f"Connection state: {state}") @room.on("disconnected") def on_disconnected() -> None: - logging.info("disconnected") + logging.info("Disconnected from room") + running.clear() + + # Initialize PlatformAudio if requested + platform_audio: Optional[rtc.PlatformAudio] = None + if args.platform_audio: + try: + platform_audio = rtc.PlatformAudio() + logging.info("PlatformAudio initialized") - @room.on("reconnecting") - def on_reconnecting() -> None: - logging.info("reconnecting") + # Select microphone if specified + if args.mic_id: + try: + platform_audio.set_recording_device(args.mic_id) + logging.info(f"Selected microphone: {args.mic_id}") + except rtc.PlatformAudioError as e: + logging.warning(f"Failed to select microphone: {e}") - @room.on("reconnected") - def on_reconnected() -> None: - logging.info("reconnected") + # Select speaker if specified + if args.speaker_id: + try: + platform_audio.set_playout_device(args.speaker_id) + logging.info(f"Selected speaker: {args.speaker_id}") + except rtc.PlatformAudioError as e: + logging.warning(f"Failed to select speaker: {e}") + except rtc.PlatformAudioError as e: + logging.error(f"Failed to initialize PlatformAudio: {e}") + logging.info("Falling back to no microphone capture") + platform_audio = None + + # Generate access token token = ( api.AccessToken() .with_identity("python-bot") @@ -132,25 +336,87 @@ def on_reconnected() -> None: .with_grants( api.VideoGrants( room_join=True, - room="my-room", + room=args.room, ) ) .to_jwt() ) - await room.connect(os.getenv("LIVEKIT_URL"), token) - logging.info("connected to room %s", room.name) - logging.info("participants: %s", room.participants) + # Connect to room + url = os.getenv("LIVEKIT_URL") + if not url: + logging.error("LIVEKIT_URL environment variable not set") + return + + logging.info(f"Connecting to room '{args.room}'...") + await room.connect(url, token) + logging.info(f"Connected to room: {room.name}") + + # Publish microphone using PlatformAudio + if platform_audio: + # PLATFORMAUDIO MODE: The ADM captures audio from the microphone and sends + # it directly to WebRTC. You cannot access the raw audio frames. + # Benefits: Built-in AEC, NS, AGC. Automatic speaker playout. + source = platform_audio.create_audio_source( + rtc.PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + ) + ) + mic_track = rtc.LocalAudioTrack.create_audio_track("microphone", source) + + publication = await room.local_participant.publish_track( + mic_track, + rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_MICROPHONE), + ) + logging.info(f"Published microphone track: {publication.sid}") + + # Publish WAV file if specified + wav_task: Optional[asyncio.Task] = None + if args.file: + wav_task = asyncio.create_task(publish_wav_file(room, args.file, running)) + + # Keep running until disconnected + logging.info("Listening for events... Press Ctrl+C to exit") await asyncio.sleep(2) - await room.local_participant.publish_data("hello world") + await room.local_participant.publish_data("hello from python!") + + while running.is_set(): + await asyncio.sleep(1) + + # Cleanup + if wav_task: + wav_task.cancel() + try: + await wav_task + except asyncio.CancelledError: + pass if __name__ == "__main__": logging.basicConfig( level=logging.INFO, - handlers=[logging.FileHandler("basic_room.log"), logging.StreamHandler()], + format="%(asctime)s - %(levelname)s - %(message)s", + handlers=[ + logging.FileHandler("basic_room.log"), + logging.StreamHandler(), + ], ) + args = parse_args() + + # Handle --list-devices + if args.list_devices: + list_audio_devices() + exit(0) + + # Validate arguments + if args.mic_id and not args.platform_audio: + logging.warning("--mic-id requires --platform-audio, ignoring") + if args.speaker_id and not args.platform_audio: + logging.warning("--speaker-id requires --platform-audio, ignoring") + loop = asyncio.get_event_loop() room = rtc.Room(loop=loop) @@ -158,7 +424,8 @@ async def cleanup(): await room.disconnect() loop.stop() - asyncio.ensure_future(main(room)) + asyncio.ensure_future(main(room, args)) + for signal in [SIGINT, SIGTERM]: loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) diff --git a/examples/data-streams/data_streams.py b/examples/data-streams/data_streams.py new file mode 100644 index 00000000..babd4ef5 --- /dev/null +++ b/examples/data-streams/data_streams.py @@ -0,0 +1,96 @@ +import os +import logging +import asyncio +from signal import SIGINT, SIGTERM +from livekit import rtc + +# Set the following environment variables with your own values +TOKEN = os.environ.get("LIVEKIT_TOKEN") +URL = os.environ.get("LIVEKIT_URL") + + +async def main(room: rtc.Room): + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + active_tasks = [] + + async def greetParticipant(identity: str): + text_writer = await room.local_participant.stream_text( + destination_identities=[identity], topic="chat" + ) + for char in "Hi! Just a friendly message": + await text_writer.write(char) + await text_writer.aclose() + + await room.local_participant.send_file( + "./green_tree_python.jpg", + destination_identities=[identity], + topic="files", + ) + + async def on_chat_message_received(reader: rtc.TextStreamReader, participant_identity: str): + full_text = await reader.read_all() + logger.info("Received chat message from %s: '%s'", participant_identity, full_text) + + async def on_welcome_image_received(reader: rtc.ByteStreamReader, participant_identity: str): + logger.info("Received image from %s: '%s'", participant_identity, reader.info.name) + with open(reader.info.name, mode="wb") as f: + async for chunk in reader: + f.write(chunk) + + f.close() + + @room.on("participant_connected") + def on_participant_connected(participant: rtc.RemoteParticipant): + logger.info("participant connected: %s %s", participant.sid, participant.identity) + asyncio.create_task(greetParticipant(participant.identity)) + + def _handle_chat_stream(reader, participant_identity): + task = asyncio.create_task(on_chat_message_received(reader, participant_identity)) + active_tasks.append(task) + task.add_done_callback(lambda _: active_tasks.remove(task)) + + room.register_text_stream_handler("chat", _handle_chat_stream) + + def _handle_welcome_image_stream(reader, participant_identity): + task = asyncio.create_task(on_welcome_image_received(reader, participant_identity)) + active_tasks.append(task) + task.add_done_callback(lambda _: active_tasks.remove(task)) + + room.register_byte_stream_handler("files", _handle_welcome_image_stream) + + # By default, autosubscribe is enabled. The participant will be subscribed to + # all published tracks in the room + await room.connect(URL, TOKEN) + logger.info("connected to room %s", room.name) + + for identity, participant in room.remote_participants.items(): + logger.info("Sending a welcome message to %s", identity) + await greetParticipant(participant.identity) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[ + logging.FileHandler("data_stream_example.log"), + logging.StreamHandler(), + ], + ) + + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + async def cleanup(): + await room.disconnect() + loop.stop() + + asyncio.ensure_future(main(room)) + for signal in [SIGINT, SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/examples/data-streams/green_tree_python.jpg b/examples/data-streams/green_tree_python.jpg new file mode 100644 index 00000000..9c49df47 --- /dev/null +++ b/examples/data-streams/green_tree_python.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a27eb74910a778fa47ae2c5ac9b0fda51b3c9f7dd26762f071b75f92da0efd3d +size 2627889 diff --git a/examples/data_tracks/publisher.py b/examples/data_tracks/publisher.py new file mode 100644 index 00000000..d3185ccd --- /dev/null +++ b/examples/data_tracks/publisher.py @@ -0,0 +1,70 @@ +import os +import logging +import asyncio +import time +from signal import SIGINT, SIGTERM +from livekit import rtc + +# Set the following environment variables with your own values +TOKEN = os.environ.get("LIVEKIT_TOKEN") +URL = os.environ.get("LIVEKIT_URL") + + +async def read_sensor() -> bytes: + # Dynamically read some sensor data... + return bytes([0xFA] * 256) + + +async def push_frames(track: rtc.LocalDataTrack): + while True: + logging.info("Pushing frame") + data = await read_sensor() + try: + frame = rtc.DataTrackFrame(payload=data, user_timestamp=int(time.time() * 1000)) + track.try_push(frame) + except rtc.PushFrameError as e: + logging.error("Failed to push frame: %s", e) + await asyncio.sleep(0.5) + + +async def main(room: rtc.Room): + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + await room.connect(URL, TOKEN) + logger.info("connected to room %s", room.name) + + track = await room.local_participant.publish_data_track(name="my_sensor_data") + await push_frames(track) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[ + logging.FileHandler("publisher.log"), + logging.StreamHandler(), + ], + ) + + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + main_task = asyncio.ensure_future(main(room)) + + async def cleanup(): + main_task.cancel() + try: + await main_task + except asyncio.CancelledError: + pass + await room.disconnect() + loop.stop() + + for signal in [SIGINT, SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/examples/data_tracks/subscriber.py b/examples/data_tracks/subscriber.py new file mode 100644 index 00000000..bc5e988f --- /dev/null +++ b/examples/data_tracks/subscriber.py @@ -0,0 +1,69 @@ +import os +import logging +import asyncio +import time +from signal import SIGINT, SIGTERM +from livekit import rtc + +# Set the following environment variables with your own values +TOKEN = os.environ.get("LIVEKIT_TOKEN") +URL = os.environ.get("LIVEKIT_URL") + + +async def subscribe(track: rtc.RemoteDataTrack): + logging.info( + "Subscribing to '%s' published by '%s'", + track.info.name, + track.publisher_identity, + ) + try: + async for frame in track.subscribe(): + logging.info("Received frame (%d bytes)", len(frame.payload)) + + if frame.user_timestamp is not None: + latency = (int(time.time() * 1000) - frame.user_timestamp) / 1000.0 + logging.info("Latency: %.3f s", latency) + except rtc.SubscribeDataTrackError as e: + logging.error("Failed to subscribe to '%s': %s", track.info.name, e.message) + + +async def main(room: rtc.Room): + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + + active_tasks = [] + + @room.on("data_track_published") + def on_data_track_published(track: rtc.RemoteDataTrack): + task = asyncio.create_task(subscribe(track)) + active_tasks.append(task) + task.add_done_callback(lambda _: active_tasks.remove(task)) + + await room.connect(URL, TOKEN) + logger.info("connected to room %s", room.name) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[ + logging.FileHandler("subscriber.log"), + logging.StreamHandler(), + ], + ) + + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + async def cleanup(): + await room.disconnect() + loop.stop() + + asyncio.ensure_future(main(room)) + for signal in [SIGINT, SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/examples/e2ee.py b/examples/e2ee.py index b9617773..96db94c8 100644 --- a/examples/e2ee.py +++ b/examples/e2ee.py @@ -1,15 +1,19 @@ import asyncio import logging +import os from signal import SIGINT, SIGTERM import numpy as np from livekit import rtc -URL = "ws://localhost:7880" -TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY" # noqa +URL = os.environ.get("LIVEKIT_URL", "ws://localhost:7880") +TOKEN = os.environ.get( + "LIVEKIT_TOKEN", + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE5MDY2MTMyODgsImlzcyI6IkFQSVRzRWZpZFpqclFvWSIsIm5hbWUiOiJuYXRpdmUiLCJuYmYiOjE2NzI2MTMyODgsInN1YiI6Im5hdGl2ZSIsInZpZGVvIjp7InJvb20iOiJ0ZXN0Iiwicm9vbUFkbWluIjp0cnVlLCJyb29tQ3JlYXRlIjp0cnVlLCJyb29tSm9pbiI6dHJ1ZSwicm9vbUxpc3QiOnRydWV9fQ.uSNIangMRu8jZD5mnRYoCHjcsQWCrJXgHCs0aNIgBFY", +) # noqa -# ("livekitrocks") this is our shared key, it must match the one used by your clients -SHARED_KEY = b"livekitrocks" +# This shared key must match the one used by your clients +SHARED_KEY = os.environ.get("E2EE_SHARED_KEY", "livekitrocks").encode() WIDTH, HEIGHT = 1280, 720 @@ -47,8 +51,7 @@ async def draw_cube(source: rtc.VideoSource): [3, 7], ] - frame = rtc.ArgbFrame.create(rtc.VideoFormatType.FORMAT_ARGB, WIDTH, HEIGHT) - arr = np.frombuffer(frame.data, dtype=np.uint8) + arr = np.zeros((WIDTH * HEIGHT * 4), dtype=np.uint8) angle = 0 while True: @@ -88,7 +91,7 @@ async def draw_cube(source: rtc.VideoSource): idx = (y + dy) * WIDTH * 4 + (x + dx) * 4 arr[idx : idx + 4] = [255, 255, 255, 255] - f = rtc.VideoFrame(frame.to_i420()) + f = rtc.VideoFrame(WIDTH, HEIGHT, rtc.VideoBufferType.ARGB, arr) source.capture_frame(f) angle += 0.02 @@ -98,9 +101,7 @@ async def draw_cube(source: rtc.VideoSource): async def main(room: rtc.Room): @room.on("e2ee_state_changed") - def on_e2ee_state_changed( - participant: rtc.Participant, state: rtc.EncryptionState - ) -> None: + def on_e2ee_state_changed(participant: rtc.Participant, state: rtc.EncryptionState) -> None: logging.info("e2ee state changed: %s %s", participant.identity, state) logging.info("connecting to %s", URL) @@ -109,7 +110,7 @@ def on_e2ee_state_changed( e2ee_options.key_provider_options.shared_key = SHARED_KEY await room.connect( - URL, TOKEN, options=rtc.RoomOptions(auto_subscribe=True, e2ee=e2ee_options) + URL, TOKEN, options=rtc.RoomOptions(auto_subscribe=True, encryption=e2ee_options) ) logging.info("connected to room %s", room.name) diff --git a/examples/face_landmark/face_landmark.py b/examples/face_landmark/face_landmark.py index edc20f36..99fe04e3 100644 --- a/examples/face_landmark/face_landmark.py +++ b/examples/face_landmark/face_landmark.py @@ -72,9 +72,7 @@ def draw_landmarks_on_image(rgb_image, detection_result): face_landmarks_proto = landmark_pb2.NormalizedLandmarkList() face_landmarks_proto.landmark.extend( [ - landmark_pb2.NormalizedLandmark( - x=landmark.x, y=landmark.y, z=landmark.z - ) + landmark_pb2.NormalizedLandmark(x=landmark.x, y=landmark.y, z=landmark.z) for landmark in face_landmarks ] ) @@ -113,9 +111,7 @@ async def frame_loop(video_stream: rtc.VideoStream) -> None: arr = arr.reshape((buffer.height, buffer.width, 3)) mp_image = mp.Image(image_format=mp.ImageFormat.SRGB, data=arr) - detection_result = landmarker.detect_for_video( - mp_image, frame_event.timestamp_us - ) + detection_result = landmarker.detect_for_video(mp_image, frame_event.timestamp_us) draw_landmarks_on_image(arr, detection_result) diff --git a/examples/local_audio/db_meter.py b/examples/local_audio/db_meter.py new file mode 100644 index 00000000..65f07d68 --- /dev/null +++ b/examples/local_audio/db_meter.py @@ -0,0 +1,262 @@ +""" +Audio dB meter utilities for LiveKit Python SDK examples. + +This module provides functions to calculate and display audio levels in decibels (dB) +from raw audio samples, useful for monitoring microphone input and room audio levels. +""" + +import math +import queue +import time +from typing import List + +# dB meter configuration constants +DB_METER_UPDATE_INTERVAL_MS = 50 # Update every 50ms +MIC_METER_WIDTH = 25 # Width of the mic dB meter bar +ROOM_METER_WIDTH = 25 # Width of the room dB meter bar + + +def calculate_db_level(samples: List[int]) -> float: + """ + Calculate decibel level from audio samples. + + Args: + samples: List of 16-bit audio samples + + Returns: + dB level as float. Returns -60.0 for silence/empty samples. + """ + if not samples: + return -60.0 # Very quiet + + # Calculate RMS (Root Mean Square) + sum_squares = sum( + (sample / 32767.0) ** 2 # Normalize to -1.0 to 1.0 range + for sample in samples + ) + + rms = math.sqrt(sum_squares / len(samples)) + + # Convert to dB (20 * log10(rms)) + if rms > 0.0: + return 20.0 * math.log10(rms) + else: + return -60.0 # Very quiet + + +def get_meter_color(db_level: float, position_ratio: float) -> str: + """ + Get ANSI color code based on dB level and position in meter. + + Args: + db_level: Current dB level + position_ratio: Position in meter (0.0 to 1.0) + + Returns: + ANSI color code string + """ + # Determine color based on both dB level and position in the meter + if db_level > -6.0 and position_ratio > 0.85: + return "\x1b[91m" # Bright red - clipping/very loud + elif db_level > -12.0 and position_ratio > 0.7: + return "\x1b[31m" # Red - loud + elif db_level > -18.0 and position_ratio > 0.5: + return "\x1b[93m" # Bright yellow - medium-loud + elif db_level > -30.0 and position_ratio > 0.3: + return "\x1b[33m" # Yellow - medium + elif position_ratio > 0.1: + return "\x1b[92m" # Bright green - low-medium + else: + return "\x1b[32m" # Green - low + + +def format_single_meter(db_level: float, meter_width: int, meter_label: str) -> str: + """ + Format a single dB meter with colors. + + Args: + db_level: dB level to display + meter_width: Width of the meter bar in characters + meter_label: Label text for the meter + + Returns: + Formatted meter string with ANSI colors + """ + # ANSI color codes + COLOR_RESET = "\x1b[0m" + COLOR_DIM = "\x1b[2m" + + db_clamped = max(-60.0, min(0.0, db_level)) + normalized = (db_clamped + 60.0) / 60.0 # Normalize to 0.0-1.0 + filled_width = int(normalized * meter_width) + + meter = meter_label + + # Add the dB value with appropriate color + if db_level > -6.0: + db_color = "\x1b[91m" # Bright red + elif db_level > -12.0: + db_color = "\x1b[31m" # Red + elif db_level > -24.0: + db_color = "\x1b[33m" # Yellow + else: + db_color = "\x1b[32m" # Green + + meter += f"{db_color}{db_level:>7.1f}{COLOR_RESET} " + + # Add the visual meter with colors + meter += "[" + for i in range(meter_width): + position_ratio = i / meter_width + + if i < filled_width: + color = get_meter_color(db_level, position_ratio) + meter += f"{color}█{COLOR_RESET}" # Full block for active levels + else: + meter += f"{COLOR_DIM}░{COLOR_RESET}" # Light shade for empty + + meter += "]" + return meter + + +def format_dual_meters(mic_db: float, room_db: float) -> str: + """ + Format both dB meters on the same line. + + Args: + mic_db: Microphone dB level + room_db: Room audio dB level + + Returns: + Formatted dual meter string + """ + mic_meter = format_single_meter(mic_db, MIC_METER_WIDTH, "Mic: ") + room_meter = format_single_meter(room_db, ROOM_METER_WIDTH, " Room: ") + + return f"{mic_meter}{room_meter}" + + +def display_dual_db_meters( + mic_db_receiver, room_db_receiver, room_name: str = "Audio Levels Monitor" +) -> None: + """ + Display dual dB meters continuously until interrupted. + + Args: + mic_db_receiver: Queue or receiver for microphone dB levels + room_db_receiver: Queue or receiver for room dB levels + room_name: Name of the room to display as the title + """ + try: + last_update = time.time() + current_mic_db = -60.0 + current_room_db = -60.0 + + print() # Start on a new line + print(f"\x1b[92mRoom [{room_name}]\x1b[0m") + print( + "\x1b[2m────────────────────────────────────────────────────────────────────────────────\x1b[0m" + ) + + while True: + # Check for new data (non-blocking) + try: + while True: # Drain all available data + mic_db = mic_db_receiver.get_nowait() + current_mic_db = mic_db + except queue.Empty: + pass # No more data available + + try: + while True: # Drain all available data + room_db = room_db_receiver.get_nowait() + current_room_db = room_db + except queue.Empty: + pass # No more data available + + # Update display at regular intervals + current_time = time.time() + if current_time - last_update >= DB_METER_UPDATE_INTERVAL_MS / 1000.0: + # Clear current line and display meters in place + print( + f"\r\x1b[K{format_dual_meters(current_mic_db, current_room_db)}", + end="", + flush=True, + ) + last_update = current_time + + # Small sleep to prevent busy waiting + time.sleep(0.01) + + except KeyboardInterrupt: + print() # Move to next line after Ctrl+C + + +def display_single_db_meter(db_receiver, label: str = "Mic Level: ") -> None: + """ + Display a single dB meter continuously until interrupted. + + Args: + db_receiver: Queue or receiver for dB levels + label: Label for the meter display + """ + try: + last_update = time.time() + current_db = -60.0 + first_display = True + + if first_display: + print() # Start on a new line + print(f"\x1b[92m{label}\x1b[0m") + print("\x1b[2m────────────────────────────────────────\x1b[0m") + first_display = False + + while True: + # Check for new data (non-blocking) + try: + while True: # Drain all available data + db_level = db_receiver.get_nowait() + current_db = db_level + except queue.Empty: + pass # No more data available + + # Update display at regular intervals + current_time = time.time() + if current_time - last_update >= DB_METER_UPDATE_INTERVAL_MS / 1000.0: + # Clear current line and display meter in place + meter = format_single_meter(current_db, 40, label) + print(f"\r\x1b[K{meter}", end="", flush=True) + last_update = current_time + + # Small sleep to prevent busy waiting + time.sleep(0.01) + + except KeyboardInterrupt: + print() # Move to next line after Ctrl+C + + +# Example usage and testing functions +def demo_db_meter() -> None: + """Demo function to test dB meter functionality.""" + import random + + # Simulate some test data + class MockReceiver: + def __init__(self): + self.data = [] + + def get_nowait(self): + if not self.data: + # Generate random dB value between -60 and 0 + self.data.append(random.uniform(-60, 0)) + return self.data.pop(0) + + mic_receiver = MockReceiver() + room_receiver = MockReceiver() + + print("Starting dB meter demo (Ctrl+C to stop)...") + display_dual_db_meters(mic_receiver, room_receiver) + + +if __name__ == "__main__": + demo_db_meter() diff --git a/examples/local_audio/full_duplex.py b/examples/local_audio/full_duplex.py new file mode 100644 index 00000000..a1f1073f --- /dev/null +++ b/examples/local_audio/full_duplex.py @@ -0,0 +1,255 @@ +""" +Full-duplex audio using PlatformAudio. + +This example demonstrates: +- Using PlatformAudio for microphone capture with built-in voice processing (AEC, NS, AGC) +- Automatic speaker playout for received audio (handled by PlatformAudio) +- Monitoring microphone dB levels via AudioStream +- Device enumeration and selection + +With PlatformAudio: +- Microphone audio is captured with AEC, NS, and AGC applied automatically +- Received audio from remote participants is played through speakers automatically +- No manual audio routing needed + +Usage: + python full_duplex.py + python full_duplex.py --list-devices + python full_duplex.py --mic-id "mic-guid" --speaker-id "speaker-guid" +""" + +import argparse +import asyncio +import logging +import os +import queue +import threading + +try: + from dotenv import find_dotenv, load_dotenv + + HAS_DOTENV = True +except ImportError: + HAS_DOTENV = False + +from livekit import api, rtc + +from db_meter import calculate_db_level, display_single_db_meter + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Full-duplex audio using PlatformAudio") + parser.add_argument( + "--list-devices", + action="store_true", + help="List available audio devices and exit", + ) + parser.add_argument( + "--mic-id", + type=str, + help="Select microphone by device ID (from --list-devices)", + ) + parser.add_argument( + "--speaker-id", + type=str, + help="Select speaker by device ID (from --list-devices)", + ) + return parser.parse_args() + + +def list_audio_devices() -> None: + """List available audio devices using PlatformAudio.""" + try: + platform_audio = rtc.PlatformAudio() + except rtc.PlatformAudioError as e: + print(f"Failed to initialize PlatformAudio: {e}") + return + + try: + print("\nRecording devices (microphones):") + for device in platform_audio.recording_devices(): + print(f" [{device.index}] {device.name}") + print(f" ID: {device.id}") + + print("\nPlayout devices (speakers):") + for device in platform_audio.playout_devices(): + print(f" [{device.index}] {device.name}") + print(f" ID: {device.id}") + + print() + finally: + platform_audio.close() + + +async def main(args: argparse.Namespace) -> None: + logging.basicConfig(level=logging.INFO) + + # Load environment variables from a .env file if present + if HAS_DOTENV: + load_dotenv(find_dotenv()) + + url = os.getenv("LIVEKIT_URL") + api_key = os.getenv("LIVEKIT_API_KEY") + api_secret = os.getenv("LIVEKIT_API_SECRET") + token = os.getenv("LIVEKIT_TOKEN") + # check for either token or api_key and api_secret + if not url or (not token and (not api_key or not api_secret)): + raise RuntimeError( + "LIVEKIT_TOKEN or LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set in env" + ) + + # Initialize PlatformAudio for microphone capture and speaker playout + # PlatformAudio provides: + # - Built-in AEC (echo cancellation), NS (noise suppression), AGC (auto gain control) + # - Automatic speaker playout for received audio + try: + platform_audio = rtc.PlatformAudio() + logging.info("PlatformAudio initialized") + except rtc.PlatformAudioError as e: + logging.error(f"Failed to initialize PlatformAudio: {e}") + return + + # Select microphone if specified + if args.mic_id: + try: + platform_audio.set_recording_device(args.mic_id) + logging.info(f"Selected microphone: {args.mic_id}") + except rtc.PlatformAudioError as e: + logging.warning(f"Failed to select microphone: {e}") + + # Select speaker if specified + if args.speaker_id: + try: + platform_audio.set_playout_device(args.speaker_id) + logging.info(f"Selected speaker: {args.speaker_id}") + except rtc.PlatformAudioError as e: + logging.warning(f"Failed to select speaker: {e}") + + room = rtc.Room() + source = None + + # dB level monitoring (mic only) + mic_db_queue: queue.Queue[float] = queue.Queue() + + # With PlatformAudio, received audio is automatically played through speakers + # We just log when tracks are subscribed/unsubscribed + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ): + if track.kind == rtc.TrackKind.KIND_AUDIO: + logging.info( + "Subscribed to audio from %s (auto-playing through speaker)", + participant.identity, + ) + + room.on("track_subscribed", on_track_subscribed) + + def on_track_unsubscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ): + if track.kind == rtc.TrackKind.KIND_AUDIO: + logging.info("Unsubscribed from audio of %s", participant.identity) + + room.on("track_unsubscribed", on_track_unsubscribed) + + # generate token if not provided + if not token: + token = ( + api.AccessToken(api_key, api_secret) + .with_identity("local-audio") + .with_name("Local Audio") + .with_grants( + api.VideoGrants( + room_join=True, + room="local-audio", + ) + ) + .to_jwt() + ) + + try: + await room.connect(url, token) + logging.info("connected to room %s", room.name) + + # Create audio source with voice processing enabled + source = platform_audio.create_audio_source( + rtc.PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + ) + ) + track = rtc.LocalAudioTrack.create_audio_track("mic", source) + + pub_opts = rtc.TrackPublishOptions() + pub_opts.source = rtc.TrackSource.SOURCE_MICROPHONE + await room.local_participant.publish_track(track, pub_opts) + logging.info("published local microphone with PlatformAudio") + + # Start dB meter display in a separate thread + meter_thread = threading.Thread( + target=display_single_db_meter, + args=(mic_db_queue,), + kwargs={"label": "Mic Level: "}, + daemon=True, + ) + meter_thread.start() + + # Monitor microphone dB levels via AudioStream + async def monitor_mic_db(): + mic_stream = rtc.AudioStream(track, sample_rate=48000, num_channels=1) + frame_count = 0 + sample_interval = 5 # Process every 5th frame to reduce load + + try: + async for frame_event in mic_stream: + # Skip frames to reduce processing load + frame_count += 1 + if frame_count % sample_interval != 0: + continue + + frame = frame_event.frame + # Convert frame data to list of samples + samples = list(frame.data) + db_level = calculate_db_level(samples) + # Update queue with latest value (non-blocking) + try: + mic_db_queue.put_nowait(db_level) + except queue.Full: + pass # Drop if queue is full + except asyncio.CancelledError: + pass + except Exception: + pass + finally: + await mic_stream.aclose() + + asyncio.create_task(monitor_mic_db()) + + # Run until Ctrl+C + while True: + await asyncio.sleep(1) + except (KeyboardInterrupt, asyncio.CancelledError): + pass + finally: + try: + await room.disconnect() + except Exception: + pass + # Clean up PlatformAudio resources + if source is not None: + source.close() + platform_audio.close() + + +if __name__ == "__main__": + args = parse_args() + + if args.list_devices: + list_audio_devices() + else: + asyncio.run(main(args)) diff --git a/examples/local_audio/list_devices.py b/examples/local_audio/list_devices.py new file mode 100644 index 00000000..a22edd15 --- /dev/null +++ b/examples/local_audio/list_devices.py @@ -0,0 +1,42 @@ +from livekit.rtc import MediaDevices + + +def main(): + # Create a MediaDevices instance + devices = MediaDevices() + + # Get default devices + default_input_idx = devices.default_input_device() + default_output_idx = devices.default_output_device() + + # List input devices + print("=== Input Devices ===") + input_devices = devices.list_input_devices() + if not input_devices: + print("No input devices found") + else: + for dev in input_devices: + default_marker = " (default)" if dev["index"] == default_input_idx else "" + print( + f" [{dev['index']}] {dev['name']}{default_marker} - " + f"{dev['max_input_channels']} channels @ {dev['default_samplerate']} Hz" + ) + + print() + + # List output devices + print("=== Output Devices ===") + output_devices = devices.list_output_devices() + if not output_devices: + print("No output devices found") + else: + for dev in output_devices: + default_marker = " (default)" if dev["index"] == default_output_idx else "" + print( + f" [{dev['index']}] {dev['name']}{default_marker} - " + f"{dev['max_output_channels']} channels @ {dev['default_samplerate']} Hz" + ) + + +if __name__ == "__main__": + main() diff --git a/examples/local_audio/publish_mic.py b/examples/local_audio/publish_mic.py new file mode 100644 index 00000000..9b6976ba --- /dev/null +++ b/examples/local_audio/publish_mic.py @@ -0,0 +1,189 @@ +""" +Publish microphone audio using PlatformAudio. + +This example demonstrates: +- Using PlatformAudio for microphone capture with built-in voice processing (AEC, NS, AGC) +- Monitoring microphone dB levels via AudioStream +- Device enumeration and selection + +Usage: + python publish_mic.py + python publish_mic.py --list-devices + python publish_mic.py --mic-id "device-guid" +""" + +import argparse +import asyncio +import logging +import os +import queue +import threading + +try: + from dotenv import find_dotenv, load_dotenv + + HAS_DOTENV = True +except ImportError: + HAS_DOTENV = False + +from livekit import api, rtc + +from db_meter import calculate_db_level, display_single_db_meter + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Publish microphone using PlatformAudio") + parser.add_argument( + "--list-devices", + action="store_true", + help="List available audio devices and exit", + ) + parser.add_argument( + "--mic-id", + type=str, + help="Select microphone by device ID (from --list-devices)", + ) + return parser.parse_args() + + +def list_audio_devices() -> None: + """List available audio devices using PlatformAudio.""" + try: + platform_audio = rtc.PlatformAudio() + except rtc.PlatformAudioError as e: + print(f"Failed to initialize PlatformAudio: {e}") + return + + try: + print("\nRecording devices (microphones):") + for device in platform_audio.recording_devices(): + print(f" [{device.index}] {device.name}") + print(f" ID: {device.id}") + + print() + finally: + platform_audio.close() + + +async def main(args: argparse.Namespace) -> None: + logging.basicConfig(level=logging.INFO) + + # Load environment variables from a .env file if present + if HAS_DOTENV: + load_dotenv(find_dotenv()) + + url = os.getenv("LIVEKIT_URL") + api_key = os.getenv("LIVEKIT_API_KEY") + api_secret = os.getenv("LIVEKIT_API_SECRET") + if not url or not api_key or not api_secret: + raise RuntimeError( + "LIVEKIT_URL and LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set in env" + ) + + # Initialize PlatformAudio for microphone capture + # PlatformAudio provides built-in AEC, NS, and AGC + try: + platform_audio = rtc.PlatformAudio() + logging.info("PlatformAudio initialized") + except rtc.PlatformAudioError as e: + logging.error(f"Failed to initialize PlatformAudio: {e}") + return + + # Select microphone if specified + if args.mic_id: + try: + platform_audio.set_recording_device(args.mic_id) + logging.info(f"Selected microphone: {args.mic_id}") + except rtc.PlatformAudioError as e: + logging.warning(f"Failed to select microphone: {e}") + + room = rtc.Room() + source = None + + # dB level monitoring + mic_db_queue: queue.Queue[float] = queue.Queue() + + token = ( + api.AccessToken(api_key, api_secret) + .with_identity("local-audio") + .with_name("Local Audio") + .with_grants( + api.VideoGrants( + room_join=True, + room="local-audio", + ) + ) + .to_jwt() + ) + + try: + await room.connect(url, token) + logging.info("connected to room %s", room.name) + + # Create audio source with voice processing enabled + source = platform_audio.create_audio_source( + rtc.PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + ) + ) + track = rtc.LocalAudioTrack.create_audio_track("mic", source) + + pub_opts = rtc.TrackPublishOptions() + pub_opts.source = rtc.TrackSource.SOURCE_MICROPHONE + await room.local_participant.publish_track(track, pub_opts) + logging.info("published local microphone with PlatformAudio") + + # Start dB meter display in a separate thread + meter_thread = threading.Thread( + target=display_single_db_meter, args=(mic_db_queue, "Mic: "), daemon=True + ) + meter_thread.start() + + # Monitor microphone dB levels via AudioStream + async def monitor_mic_db(): + mic_stream = rtc.AudioStream(track, sample_rate=48000, num_channels=1) + try: + async for frame_event in mic_stream: + frame = frame_event.frame + # Convert frame data to list of samples + samples = list(frame.data) + db_level = calculate_db_level(samples) + # Update queue with latest value (non-blocking) + try: + mic_db_queue.put_nowait(db_level) + except queue.Full: + pass # Drop if queue is full + except asyncio.CancelledError: + pass + except Exception: + pass + finally: + await mic_stream.aclose() + + asyncio.create_task(monitor_mic_db()) + + # Run until Ctrl+C + while True: + await asyncio.sleep(1) + except (KeyboardInterrupt, asyncio.CancelledError): + pass + finally: + try: + await room.disconnect() + except Exception: + pass + # Clean up PlatformAudio resources + if source is not None: + source.close() + platform_audio.close() + + +if __name__ == "__main__": + args = parse_args() + + if args.list_devices: + list_audio_devices() + else: + asyncio.run(main(args)) diff --git a/examples/local_video/README.md b/examples/local_video/README.md new file mode 100644 index 00000000..f532c430 --- /dev/null +++ b/examples/local_video/README.md @@ -0,0 +1,67 @@ +# Local Video Packet-Trailer Examples + +These desktop examples show how to publish a local camera track and attach packet-trailer frame metadata from Python. + +## Setup + +```bash +export LIVEKIT_URL=https://your-livekit-host +export LIVEKIT_API_KEY=your-api-key +export LIVEKIT_API_SECRET=your-api-secret +``` + +Run these examples from the repository root with `uv run --project examples/local_video`. +LiveKit connection settings can also be passed with `--url`, `--api-key`, and `--api-secret`. + +## Publisher + +Publish a camera track: + +```bash +uv run --project examples/local_video python examples/local_video/publisher.py \ + --room-name demo \ + --identity py-cam \ + --camera-index 0 +``` + +Attach packet-trailer metadata: + +```bash +uv run --project examples/local_video python examples/local_video/publisher.py \ + --room-name demo \ + --identity py-cam \ + --attach-timestamp \ + --attach-frame-id +``` + +Useful flags: + +- `--camera-index `: OpenCV camera index to publish. +- `--width ` / `--height `: requested camera resolution. +- `--fps `: requested publish frame rate. +- `--attach-timestamp`: attach wall-clock microseconds since Unix epoch as `FrameMetadata.user_timestamp`. +- `--attach-frame-id`: attach a monotonically increasing `FrameMetadata.frame_id`. + +## Subscriber + +Render the first video track in the room: + +```bash +uv run --project examples/local_video python examples/local_video/subscriber.py \ + --room-name demo \ + --identity py-viewer +``` + +Display packet-trailer metadata over the video: + +```bash +uv run --project examples/local_video python examples/local_video/subscriber.py \ + --room-name demo \ + --identity py-viewer \ + --display-timestamp +``` + +Use `--participant py-cam` to only subscribe to video from a specific participant identity. +The subscriber keeps running across unpublish/republish cycles and will attach to the next matching video track. + +Press `q` in the video window or `Ctrl+C` in the terminal to exit. diff --git a/examples/local_video/publisher.py b/examples/local_video/publisher.py new file mode 100644 index 00000000..0372eab7 --- /dev/null +++ b/examples/local_video/publisher.py @@ -0,0 +1,293 @@ +from __future__ import annotations + +import argparse +import asyncio +import logging +import os +import signal +import time +from enum import Enum + +import numpy as np +from livekit import api, rtc + +try: + import cv2 +except ImportError as exc: + raise SystemExit( + "opencv-python is required to run this example. " + "Run it with `uv run --project examples/local_video python examples/local_video/publisher.py`." + ) from exc + + +class FrameFormat(str, Enum): + """Frame format to use for publishing.""" + + RGBA = "rgba" + I420 = "i420" + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Publish a local camera track with optional frame metadata.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("--camera-index", type=int, default=0, help="OpenCV camera index to use") + parser.add_argument("--width", type=int, default=1280, help="Requested capture width") + parser.add_argument("--height", type=int, default=720, help="Requested capture height") + parser.add_argument("--fps", type=float, default=30.0, help="Requested publish frame rate") + parser.add_argument("--room-name", default="video-room", help="LiveKit room name") + parser.add_argument("--identity", default="python-camera-pub", help="Participant identity") + parser.add_argument("--url", help="LiveKit server URL; falls back to LIVEKIT_URL") + parser.add_argument("--api-key", help="LiveKit API key; falls back to LIVEKIT_API_KEY") + parser.add_argument( + "--api-secret", + help="LiveKit API secret; falls back to LIVEKIT_API_SECRET", + ) + parser.add_argument( + "--attach-timestamp", + action="store_true", + help="Attach wall-clock microseconds in FrameMetadata.user_timestamp", + ) + parser.add_argument( + "--attach-frame-id", + action="store_true", + help="Attach a monotonically increasing FrameMetadata.frame_id", + ) + parser.add_argument( + "--format", + type=FrameFormat, + default=FrameFormat.I420, + choices=list(FrameFormat), + help="Frame format: 'i420' (default, efficient) or 'rgba' (original behavior)", + ) + return parser.parse_args() + + +def _require_connection(args: argparse.Namespace) -> tuple[str, str, str]: + url = args.url or os.getenv("LIVEKIT_URL") + api_key = args.api_key or os.getenv("LIVEKIT_API_KEY") + api_secret = args.api_secret or os.getenv("LIVEKIT_API_SECRET") + + missing = [ + name + for name, value in ( + ("LIVEKIT_URL or --url", url), + ("LIVEKIT_API_KEY or --api-key", api_key), + ("LIVEKIT_API_SECRET or --api-secret", api_secret), + ) + if not value + ] + if missing: + raise RuntimeError(f"Missing LiveKit connection settings: {', '.join(missing)}") + + return url, api_key, api_secret + + +def _create_token(args: argparse.Namespace, api_key: str, api_secret: str) -> str: + return ( + api.AccessToken(api_key, api_secret) + .with_identity(args.identity) + .with_name(args.identity) + .with_grants( + api.VideoGrants( + room_join=True, + room=args.room_name, + can_publish=True, + can_subscribe=False, + ) + ) + .to_jwt() + ) + + +def _open_camera(args: argparse.Namespace) -> tuple[cv2.VideoCapture, int, int]: + if args.fps <= 0: + raise RuntimeError("--fps must be greater than zero") + + capture = cv2.VideoCapture(args.camera_index) + if not capture.isOpened(): + raise RuntimeError(f"Could not open camera index {args.camera_index}") + + capture.set(cv2.CAP_PROP_FRAME_WIDTH, args.width) + capture.set(cv2.CAP_PROP_FRAME_HEIGHT, args.height) + capture.set(cv2.CAP_PROP_FPS, args.fps) + + ok, frame = capture.read() + if not ok or frame is None: + capture.release() + raise RuntimeError(f"Could not read from camera index {args.camera_index}") + + height, width = frame.shape[:2] + logging.info("camera opened at %sx%s", width, height) + return capture, width, height + + +def _frame_metadata_features(args: argparse.Namespace) -> list[int]: + features = [] + if args.attach_timestamp: + features.append(rtc.FrameMetadataFeature.FMF_USER_TIMESTAMP) + if args.attach_frame_id: + features.append(rtc.FrameMetadataFeature.FMF_FRAME_ID) + return features + + +def _metadata_for_frame( + args: argparse.Namespace, + *, + user_timestamp: int, + frame_id: int, +) -> rtc.FrameMetadata | None: + if not args.attach_timestamp and not args.attach_frame_id: + return None + + metadata = rtc.FrameMetadata() + if args.attach_timestamp: + metadata.user_timestamp = user_timestamp + if args.attach_frame_id: + metadata.frame_id = frame_id + return metadata + + +def _unix_time_us() -> int: + return time.time_ns() // 1_000 + + +def _install_signal_handlers(stop_event: asyncio.Event) -> None: + loop = asyncio.get_running_loop() + for sig in (signal.SIGINT, signal.SIGTERM): + try: + loop.add_signal_handler(sig, stop_event.set) + except (NotImplementedError, RuntimeError): + pass + + +def _bgr_to_i420(bgr: np.ndarray, width: int, height: int) -> bytes: + """Convert BGR image to I420 (YUV420p) format efficiently.""" + # OpenCV's COLOR_BGR2YUV_I420 outputs planar YUV420p (I420) + # Layout: Y plane (width*height), U plane (width/2 * height/2), V plane (width/2 * height/2) + yuv = cv2.cvtColor(bgr, cv2.COLOR_BGR2YUV_I420) + return yuv.tobytes() + + +async def _capture_loop( + args: argparse.Namespace, + capture: cv2.VideoCapture, + source: rtc.VideoSource, + width: int, + height: int, + stop_event: asyncio.Event, +) -> None: + interval = 1.0 / args.fps + next_frame_at = time.perf_counter() + started_at_ns = time.perf_counter_ns() + frame_id = 1 + submitted = 0 + last_log_at = time.perf_counter() + use_i420 = args.format == FrameFormat.I420 + + logging.info("Using frame format: %s", args.format.value) + + while not stop_event.is_set(): + ok, bgr = await asyncio.to_thread(capture.read) + if not ok or bgr is None: + logging.warning("camera frame read failed") + await asyncio.sleep(0.1) + continue + + if bgr.shape[1] != width or bgr.shape[0] != height: + bgr = cv2.resize(bgr, (width, height), interpolation=cv2.INTER_AREA) + + user_timestamp = _unix_time_us() + timestamp_us = (time.perf_counter_ns() - started_at_ns) // 1_000 + + if use_i420: + # Direct BGR -> I420 conversion (single step, more efficient) + i420_data = _bgr_to_i420(bgr, width, height) + frame = rtc.VideoFrame(width, height, rtc.VideoBufferType.I420, i420_data) + else: + # Original path: BGR -> RGBA -> I420 (in FFI layer) + rgba = cv2.cvtColor(bgr, cv2.COLOR_BGR2RGBA) + rgba = np.ascontiguousarray(rgba) + frame = rtc.VideoFrame(width, height, rtc.VideoBufferType.RGBA, rgba.tobytes()) + + metadata = _metadata_for_frame( + args, + user_timestamp=user_timestamp, + frame_id=frame_id, + ) + source.capture_frame(frame, timestamp_us=timestamp_us, metadata=metadata) + + submitted += 1 + if args.attach_frame_id: + frame_id = (frame_id + 1) & 0xFFFFFFFF + + now = time.perf_counter() + if now - last_log_at >= 2.0: + logging.info( + "published %s frames at ~%.1f fps (format=%s)", + submitted, + submitted / (now - last_log_at), + args.format.value, + ) + submitted = 0 + last_log_at = now + + next_frame_at += interval + sleep_for = next_frame_at - time.perf_counter() + if sleep_for > 0: + await asyncio.sleep(sleep_for) + else: + next_frame_at = time.perf_counter() + + +async def run(args: argparse.Namespace, stop_event: asyncio.Event) -> None: + url, api_key, api_secret = _require_connection(args) + capture, width, height = _open_camera(args) + room = rtc.Room() + source: rtc.VideoSource | None = None + + try: + token = _create_token(args, api_key, api_secret) + logging.info("connecting to room %s as %s", args.room_name, args.identity) + await room.connect(url, token) + logging.info("connected to room %s", room.name) + + source = rtc.VideoSource(width, height) + track = rtc.LocalVideoTrack.create_video_track("camera", source) + options = rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + video_encoding=rtc.VideoEncoding( + max_framerate=args.fps, + max_bitrate=3_000_000, + ), + frame_metadata_features=_frame_metadata_features(args), + ) + publication = await room.local_participant.publish_track(track, options) + logging.info( + "published camera track %s with frame metadata features %s", + publication.sid, + list(publication.frame_metadata_features), + ) + + await _capture_loop(args, capture, source, width, height, stop_event) + finally: + capture.release() + if source is not None: + await source.aclose() + await room.disconnect() + + +async def main() -> None: + logging.basicConfig(level=logging.INFO) + args = parse_args() + stop_event = asyncio.Event() + _install_signal_handlers(stop_event) + await run(args, stop_event) + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + pass diff --git a/examples/local_video/pyproject.toml b/examples/local_video/pyproject.toml new file mode 100644 index 00000000..e45cdcf2 --- /dev/null +++ b/examples/local_video/pyproject.toml @@ -0,0 +1,19 @@ +[project] +name = "livekit-local-video-example" +version = "0.1.0" +description = "Local video packet-trailer examples for the LiveKit Python SDK" +requires-python = ">=3.9" +dependencies = [ + "livekit", + "livekit-api", + "numpy", + "opencv-python", +] + +[tool.uv] +package = false + +[tool.uv.sources] +livekit = { path = "../../livekit-rtc", editable = true } +livekit-api = { path = "../../livekit-api", editable = true } +livekit-protocol = { path = "../../livekit-protocol", editable = true } diff --git a/examples/local_video/subscriber.py b/examples/local_video/subscriber.py new file mode 100644 index 00000000..c2464e2f --- /dev/null +++ b/examples/local_video/subscriber.py @@ -0,0 +1,347 @@ +from __future__ import annotations + +import argparse +import asyncio +from dataclasses import dataclass +from datetime import datetime, timezone +import logging +import os +import signal +import time + +import numpy as np +from livekit import api, rtc + +try: + import cv2 +except ImportError as exc: + raise SystemExit( + "opencv-python is required to run this example. " + "Run it with `uv run --project examples/local_video python examples/local_video/subscriber.py`." + ) from exc + + +WINDOW_NAME = "livekit_video" + + +@dataclass(frozen=True) +class SubscribedVideoTrack: + track: rtc.Track + publication: rtc.RemoteTrackPublication + participant: rtc.RemoteParticipant + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Subscribe to a LiveKit video track and optionally display frame metadata.", + formatter_class=argparse.ArgumentDefaultsHelpFormatter, + ) + parser.add_argument("--room-name", default="video-room", help="LiveKit room name") + parser.add_argument( + "--identity", default="python-video-subscriber", help="Participant identity" + ) + parser.add_argument( + "--participant", + help="Only subscribe to video from this participant identity", + ) + parser.add_argument("--url", help="LiveKit server URL; falls back to LIVEKIT_URL") + parser.add_argument("--api-key", help="LiveKit API key; falls back to LIVEKIT_API_KEY") + parser.add_argument( + "--api-secret", + help="LiveKit API secret; falls back to LIVEKIT_API_SECRET", + ) + parser.add_argument( + "--display-timestamp", + action="store_true", + help="Overlay frame ID, publisher timestamp, receive timestamp, and latency", + ) + return parser.parse_args() + + +def _require_connection(args: argparse.Namespace) -> tuple[str, str, str]: + url = args.url or os.getenv("LIVEKIT_URL") + api_key = args.api_key or os.getenv("LIVEKIT_API_KEY") + api_secret = args.api_secret or os.getenv("LIVEKIT_API_SECRET") + + missing = [ + name + for name, value in ( + ("LIVEKIT_URL or --url", url), + ("LIVEKIT_API_KEY or --api-key", api_key), + ("LIVEKIT_API_SECRET or --api-secret", api_secret), + ) + if not value + ] + if missing: + raise RuntimeError(f"Missing LiveKit connection settings: {', '.join(missing)}") + + return url, api_key, api_secret + + +def _create_token(args: argparse.Namespace, api_key: str, api_secret: str) -> str: + return ( + api.AccessToken(api_key, api_secret) + .with_identity(args.identity) + .with_name(args.identity) + .with_grants( + api.VideoGrants( + room_join=True, + room=args.room_name, + can_publish=False, + can_subscribe=True, + ) + ) + .to_jwt() + ) + + +def _unix_time_us() -> int: + return time.time_ns() // 1_000 + + +def _install_signal_handlers(stop_event: asyncio.Event) -> None: + loop = asyncio.get_running_loop() + for sig in (signal.SIGINT, signal.SIGTERM): + try: + loop.add_signal_handler(sig, stop_event.set) + except (NotImplementedError, RuntimeError): + pass + + +def _metadata_field(metadata: rtc.FrameMetadata | None, field: str) -> int | None: + if metadata is None: + return None + if not metadata.HasField(field): + return None + return getattr(metadata, field) + + +def _format_timestamp_us(timestamp_us: int | None) -> str: + if timestamp_us is None: + return "N/A" + try: + timestamp = datetime.fromtimestamp(timestamp_us / 1_000_000, tz=timezone.utc) + except (OverflowError, OSError, ValueError): + return f"" + return f"{timestamp:%Y-%m-%d %H:%M:%S}.{timestamp.microsecond // 1_000:03d}Z" + + +def _format_latency_us(receive_us: int, publish_us: int | None) -> str: + if publish_us is None: + return "N/A" + return f"{max(receive_us - publish_us, 0) / 1_000:.1f}ms" + + +def _draw_timestamp_overlay( + image: np.ndarray, + *, + metadata: rtc.FrameMetadata | None, + receive_us: int, +) -> None: + publish_us = _metadata_field(metadata, "user_timestamp") + frame_id = _metadata_field(metadata, "frame_id") + lines = [ + f"Frame ID: {frame_id if frame_id is not None else 'N/A'}", + f"Sensor: {_format_timestamp_us(publish_us)}", + f"Receive: {_format_timestamp_us(receive_us)}", + f"E2E Latency: {_format_latency_us(receive_us, publish_us)}", + ] + + font_face = cv2.FONT_HERSHEY_SIMPLEX + font_scale = 0.55 + thickness = 1 + line_height = 22 + padding = 8 + text_sizes = [cv2.getTextSize(line, font_face, font_scale, thickness)[0] for line in lines] + box_width = max(width for width, _ in text_sizes) + (padding * 2) + box_height = (line_height * len(lines)) + (padding * 2) + cv2.rectangle(image, (8, 8), (8 + box_width, 8 + box_height), (0, 0, 0), -1) + + y = 8 + padding + 16 + for line in lines: + cv2.putText( + image, + line, + (8 + padding, y), + font_face, + font_scale, + (255, 255, 255), + thickness, + cv2.LINE_AA, + ) + y += line_height + + +def _feature_names(features: list[int]) -> str: + names = [] + for feature in features: + try: + names.append(rtc.FrameMetadataFeature.Name(feature)) + except ValueError: + names.append(str(feature)) + return ", ".join(names) or "none" + + +def _window_is_open() -> bool: + try: + return cv2.getWindowProperty(WINDOW_NAME, cv2.WND_PROP_VISIBLE) >= 1 + except cv2.error: + return False + + +async def _next_video_track( + track_queue: asyncio.Queue[SubscribedVideoTrack], + stop_event: asyncio.Event, +) -> SubscribedVideoTrack | None: + while not stop_event.is_set(): + try: + return await asyncio.wait_for(track_queue.get(), timeout=0.5) + except asyncio.TimeoutError: + continue + return None + + +async def _render_video( + video_stream: rtc.VideoStream, + args: argparse.Namespace, + stop_event: asyncio.Event, + active_track_gone: asyncio.Event, +) -> None: + cv2.namedWindow(WINDOW_NAME, cv2.WINDOW_AUTOSIZE) + + try: + while not stop_event.is_set() and not active_track_gone.is_set(): + try: + frame_event = await asyncio.wait_for(video_stream.__anext__(), timeout=0.5) + except asyncio.TimeoutError: + if cv2.waitKey(1) & 0xFF == ord("q"): + stop_event.set() + continue + except StopAsyncIteration: + break + + receive_us = _unix_time_us() + frame = frame_event.frame + rgb = np.frombuffer(frame.data, dtype=np.uint8).reshape((frame.height, frame.width, 3)) + image = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) + + if args.display_timestamp: + _draw_timestamp_overlay( + image, + metadata=frame_event.metadata, + receive_us=receive_us, + ) + + cv2.imshow(WINDOW_NAME, image) + if cv2.waitKey(1) & 0xFF == ord("q"): + stop_event.set() + if not _window_is_open(): + stop_event.set() + finally: + cv2.destroyAllWindows() + + +async def run(args: argparse.Namespace, stop_event: asyncio.Event) -> None: + url, api_key, api_secret = _require_connection(args) + room = rtc.Room() + track_queue: asyncio.Queue[SubscribedVideoTrack] = asyncio.Queue() + active_publication_sid: str | None = None + active_track_gone = asyncio.Event() + video_stream: rtc.VideoStream | None = None + + @room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + if track.kind != rtc.TrackKind.KIND_VIDEO: + return + if args.participant and participant.identity != args.participant: + logging.info( + "skipping video track from %s; waiting for %s", + participant.identity, + args.participant, + ) + return + + track_queue.put_nowait( + SubscribedVideoTrack( + track=track, + publication=publication, + participant=participant, + ) + ) + + @room.on("track_unsubscribed") + def on_track_unsubscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal active_publication_sid + if publication.sid == active_publication_sid: + logging.info("active video track unsubscribed: %s", publication.sid) + active_track_gone.set() + + @room.on("track_unpublished") + def on_track_unpublished( + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal active_publication_sid + if publication.sid == active_publication_sid: + logging.info("active video track unpublished: %s", publication.sid) + active_track_gone.set() + + try: + token = _create_token(args, api_key, api_secret) + logging.info("connecting to room %s as %s", args.room_name, args.identity) + await room.connect(url, token) + logging.info("connected to room %s", room.name) + + while not stop_event.is_set(): + logging.info("waiting for a video track") + subscribed = await _next_video_track(track_queue, stop_event) + if subscribed is None: + break + + active_publication_sid = subscribed.publication.sid + active_track_gone = asyncio.Event() + logging.info( + "subscribed to %s from %s with frame metadata features: %s", + subscribed.publication.sid, + subscribed.participant.identity, + _feature_names(list(subscribed.publication.frame_metadata_features)), + ) + + video_stream = rtc.VideoStream.from_track( + track=subscribed.track, + format=rtc.VideoBufferType.RGB24, + capacity=1, + ) + try: + await _render_video(video_stream, args, stop_event, active_track_gone) + finally: + await video_stream.aclose() + video_stream = None + active_publication_sid = None + finally: + if video_stream is not None: + await video_stream.aclose() + await room.disconnect() + + +async def main() -> None: + logging.basicConfig(level=logging.INFO) + args = parse_args() + stop_event = asyncio.Event() + _install_signal_handlers(stop_event) + await run(args, stop_event) + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + pass diff --git a/examples/local_video/uv.lock b/examples/local_video/uv.lock new file mode 100644 index 00000000..25e20f08 --- /dev/null +++ b/examples/local_video/uv.lock @@ -0,0 +1,1439 @@ +version = 1 +revision = 2 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] + +[[package]] +name = "aiofiles" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "aiohappyeyeballs" }, + { name = "aiosignal" }, + { name = "async-timeout", marker = "python_full_version < '3.11'" }, + { name = "attrs" }, + { name = "frozenlist" }, + { name = "multidict" }, + { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "yarl", version = "1.22.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "yarl", version = "1.23.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a5/630bc484695d4a1342bbae85fb8689bf979106525684fc88f05b397324ad/aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf", size = 752872, upload-time = "2026-03-31T22:00:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b8/6a19dda37fda94a9ebefb3c1ae0ff419ac7fbf4fb40750e992829fc13614/aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1", size = 504582, upload-time = "2026-03-31T22:00:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/d5/34/8413eafee3421ade2d6ce9e7c0da1213e1d7f0049be09dcdc342b03a39ba/aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10", size = 499094, upload-time = "2026-03-31T22:00:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/da/cf/c6f97006093d1e8ca40fbab843ff49ec7725ab668f0714dd1cb702c62cbd/aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f", size = 1669505, upload-time = "2026-03-31T22:00:24.01Z" }, + { url = "https://files.pythonhosted.org/packages/c2/27/3b2288e66dcec8b04771b2bee3909f70e4072bea995cde5ab7e775e73ddc/aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b", size = 1648928, upload-time = "2026-03-31T22:00:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7f/605d766887594a88dcc27a19663499c7c5e13e7aa87f129b763765a2ee63/aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643", size = 1731800, upload-time = "2026-03-31T22:00:29.603Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/5a878e728e30699d22b118f1a6ad576ab6fff9eb2c6fc8a7faa9376a1c3e/aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031", size = 1824247, upload-time = "2026-03-31T22:00:32.139Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/84b448291e9996bb83bf4fad3a71a9786d542f19c50a3ff0531bfaba6fac/aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258", size = 1670742, upload-time = "2026-03-31T22:00:34.788Z" }, + { url = "https://files.pythonhosted.org/packages/14/a8/d8d5d1ab6d29a4a3bdb9db31f161e338bfdf6638f6574ea8380f1d4a243c/aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a", size = 1562474, upload-time = "2026-03-31T22:00:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/92/e8/bd889697916f10b65524422c61b4eeaf919eb35a170290cccb680cbe4eb4/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88", size = 1642235, upload-time = "2026-03-31T22:00:40.541Z" }, + { url = "https://files.pythonhosted.org/packages/60/42/3f1928107131f1413a5972ace14ddcd5364968e9bd7b3ad71272defafc9c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0", size = 1655397, upload-time = "2026-03-31T22:00:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b2/79/c4bbcf4cac3a4715a326e49720ccdc3a4b5e14a367c5029eae7727d06029/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f", size = 1703509, upload-time = "2026-03-31T22:00:45.908Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e6/32d245876f211a7308a7d5437707f9296b1f9837a2888a407ed04e61321c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8", size = 1550098, upload-time = "2026-03-31T22:00:49.48Z" }, + { url = "https://files.pythonhosted.org/packages/db/62/ab0f1304def56ce2356e6fbb9f0b024d6544010351430070f48f53b89e0a/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f", size = 1724326, upload-time = "2026-03-31T22:00:52.165Z" }, + { url = "https://files.pythonhosted.org/packages/c4/9a/aab4469689024046220ea438aa020ea2ae04cd1dd71aea3057e094f8c357/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b", size = 1658824, upload-time = "2026-03-31T22:00:55.122Z" }, + { url = "https://files.pythonhosted.org/packages/b0/98/bcc35d4db687acabf06d41f561a99fa88bca145292513388c858d99b72c5/aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83", size = 440302, upload-time = "2026-03-31T22:00:57.673Z" }, + { url = "https://files.pythonhosted.org/packages/25/61/b0203c2ef6bd268fca0eda142f0efbba7cbebd7ad38f7bb01dd31c2ff68e/aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67", size = 463076, upload-time = "2026-03-31T22:01:00.264Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/82a0fe20a541c03148528be8cac2408564a6c9a0cc7e9171802bc1d26985/attrs-26.1.0.tar.gz", hash = "sha256:d03ceb89cb322a8fd706d4fb91940737b6642aa36998fe130a9bc96c985eff32", size = 952055, upload-time = "2026-03-19T14:22:25.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/64/b4/17d4b0b2a2dc85a6df63d1157e028ed19f90d4cd97c36717afef2bc2f395/attrs-26.1.0-py3-none-any.whl", hash = "sha256:c647aa4a12dfbad9333ca4e71fe62ddc36f4e63b2d260a37a8b83d2f043ac309", size = 67548, upload-time = "2026-03-19T14:22:23.645Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, + { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, + { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, + { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "idna" +version = "3.14" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/b1/efac073e0c297ecf2fb33c346989a529d4e19164f1759102dee5953ee17e/idna-3.14.tar.gz", hash = "sha256:466d810d7a2cc1022bea9b037c39728d51ae7dad40d480fc9b7d7ecf98ba8ee3", size = 198272, upload-time = "2026-05-10T20:32:15.935Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/3c/3f62dee257eb3d6b2c1ef2a09d36d9793c7111156a73b5654d2c2305e5ce/idna-3.14-py3-none-any.whl", hash = "sha256:e677eaf072e290f7b725f9acf0b3a2bd55f9fd6f7c70abe5f0e34823d0accf69", size = 72184, upload-time = "2026-05-10T20:32:14.295Z" }, +] + +[[package]] +name = "livekit" +source = { editable = "../../livekit-rtc" } +dependencies = [ + { name = "aiofiles" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "7.34.1.20260508", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=24" }, + { name = "numpy", specifier = ">=1.26" }, + { name = "protobuf", specifier = ">=4.25.0" }, + { name = "types-protobuf", specifier = ">=3" }, +] + +[[package]] +name = "livekit-api" +source = { editable = "../../livekit-api" } +dependencies = [ + { name = "aiohttp" }, + { name = "livekit-protocol" }, + { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyjwt" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "7.34.1.20260508", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.9.0" }, + { name = "livekit-protocol", editable = "../../livekit-protocol" }, + { name = "protobuf", specifier = ">=4" }, + { name = "pyjwt", specifier = ">=2.0.0" }, + { name = "types-protobuf", specifier = ">=4" }, +] + +[[package]] +name = "livekit-local-video-example" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "livekit" }, + { name = "livekit-api" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "opencv-python" }, +] + +[package.metadata] +requires-dist = [ + { name = "livekit", editable = "../../livekit-rtc" }, + { name = "livekit-api", editable = "../../livekit-api" }, + { name = "numpy" }, + { name = "opencv-python" }, +] + +[[package]] +name = "livekit-protocol" +source = { editable = "../../livekit-protocol" } +dependencies = [ + { name = "protobuf", version = "6.33.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "7.34.1.20260508", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "protobuf", specifier = ">=4" }, + { name = "types-protobuf", specifier = ">=4" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, + { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, + { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, + { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/d7/9f/b8cef5bffa569759033adda9481211426f12f53299629b410340795c2514/numpy-2.4.4.tar.gz", hash = "sha256:2d390634c5182175533585cc89f3608a4682ccb173cc9bb940b2881c8d6f8fa0", size = 20731587, upload-time = "2026-03-29T13:22:01.298Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/c6/4218570d8c8ecc9704b5157a3348e486e84ef4be0ed3e38218ab473c83d2/numpy-2.4.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f983334aea213c99992053ede6168500e5f086ce74fbc4acc3f2b00f5762e9db", size = 16976799, upload-time = "2026-03-29T13:18:15.438Z" }, + { url = "https://files.pythonhosted.org/packages/dd/92/b4d922c4a5f5dab9ed44e6153908a5c665b71acf183a83b93b690996e39b/numpy-2.4.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:72944b19f2324114e9dc86a159787333b77874143efcf89a5167ef83cfee8af0", size = 14971552, upload-time = "2026-03-29T13:18:18.606Z" }, + { url = "https://files.pythonhosted.org/packages/8a/dc/df98c095978fa6ee7b9a9387d1d58cbb3d232d0e69ad169a4ce784bde4fd/numpy-2.4.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:86b6f55f5a352b48d7fbfd2dbc3d5b780b2d79f4d3c121f33eb6efb22e9a2015", size = 5476566, upload-time = "2026-03-29T13:18:21.532Z" }, + { url = "https://files.pythonhosted.org/packages/28/34/b3fdcec6e725409223dd27356bdf5a3c2cc2282e428218ecc9cb7acc9763/numpy-2.4.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:ba1f4fc670ed79f876f70082eff4f9583c15fb9a4b89d6188412de4d18ae2f40", size = 6806482, upload-time = "2026-03-29T13:18:23.634Z" }, + { url = "https://files.pythonhosted.org/packages/68/62/63417c13aa35d57bee1337c67446761dc25ea6543130cf868eace6e8157b/numpy-2.4.4-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a87ec22c87be071b6bdbd27920b129b94f2fc964358ce38f3822635a3e2e03d", size = 15973376, upload-time = "2026-03-29T13:18:26.677Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c5/9fcb7e0e69cef59cf10c746b84f7d58b08bc66a6b7d459783c5a4f6101a6/numpy-2.4.4-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:df3775294accfdd75f32c74ae39fcba920c9a378a2fc18a12b6820aa8c1fb502", size = 16925137, upload-time = "2026-03-29T13:18:30.14Z" }, + { url = "https://files.pythonhosted.org/packages/7e/43/80020edacb3f84b9efdd1591120a4296462c23fd8db0dde1666f6ef66f13/numpy-2.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0d4e437e295f18ec29bc79daf55e8a47a9113df44d66f702f02a293d93a2d6dd", size = 17329414, upload-time = "2026-03-29T13:18:33.733Z" }, + { url = "https://files.pythonhosted.org/packages/fd/06/af0658593b18a5f73532d377188b964f239eb0894e664a6c12f484472f97/numpy-2.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6aa3236c78803afbcb255045fbef97a9e25a1f6c9888357d205ddc42f4d6eba5", size = 18658397, upload-time = "2026-03-29T13:18:37.511Z" }, + { url = "https://files.pythonhosted.org/packages/e6/ce/13a09ed65f5d0ce5c7dd0669250374c6e379910f97af2c08c57b0608eee4/numpy-2.4.4-cp311-cp311-win32.whl", hash = "sha256:30caa73029a225b2d40d9fae193e008e24b2026b7ee1a867b7ee8d96ca1a448e", size = 6239499, upload-time = "2026-03-29T13:18:40.372Z" }, + { url = "https://files.pythonhosted.org/packages/bd/63/05d193dbb4b5eec1eca73822d80da98b511f8328ad4ae3ca4caf0f4db91d/numpy-2.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:6bbe4eb67390b0a0265a2c25458f6b90a409d5d069f1041e6aff1e27e3d9a79e", size = 12614257, upload-time = "2026-03-29T13:18:42.95Z" }, + { url = "https://files.pythonhosted.org/packages/87/c5/8168052f080c26fa984c413305012be54741c9d0d74abd7fbeeccae3889f/numpy-2.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:fcfe2045fd2e8f3cb0ce9d4ba6dba6333b8fa05bb8a4939c908cd43322d14c7e", size = 10486775, upload-time = "2026-03-29T13:18:45.835Z" }, + { url = "https://files.pythonhosted.org/packages/28/05/32396bec30fb2263770ee910142f49c1476d08e8ad41abf8403806b520ce/numpy-2.4.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15716cfef24d3a9762e3acdf87e27f58dc823d1348f765bbea6bef8c639bfa1b", size = 16689272, upload-time = "2026-03-29T13:18:49.223Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f3/a983d28637bfcd763a9c7aafdb6d5c0ebf3d487d1e1459ffdb57e2f01117/numpy-2.4.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:23cbfd4c17357c81021f21540da84ee282b9c8fba38a03b7b9d09ba6b951421e", size = 14699573, upload-time = "2026-03-29T13:18:52.629Z" }, + { url = "https://files.pythonhosted.org/packages/9b/fd/e5ecca1e78c05106d98028114f5c00d3eddb41207686b2b7de3e477b0e22/numpy-2.4.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:8b3b60bb7cba2c8c81837661c488637eee696f59a877788a396d33150c35d842", size = 5204782, upload-time = "2026-03-29T13:18:55.579Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/702a4594413c1a8632092beae8aba00f1d67947389369b3777aed783fdca/numpy-2.4.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:e4a010c27ff6f210ff4c6ef34394cd61470d01014439b192ec22552ee867f2a8", size = 6552038, upload-time = "2026-03-29T13:18:57.769Z" }, + { url = "https://files.pythonhosted.org/packages/7f/37/eed308a8f56cba4d1fdf467a4fc67ef4ff4bf1c888f5fc980481890104b1/numpy-2.4.4-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f9e75681b59ddaa5e659898085ae0eaea229d054f2ac0c7e563a62205a700121", size = 15670666, upload-time = "2026-03-29T13:19:00.341Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0d/0e3ecece05b7a7e87ab9fb587855548da437a061326fff64a223b6dcb78a/numpy-2.4.4-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:81f4a14bee47aec54f883e0cad2d73986640c1590eb9bfaaba7ad17394481e6e", size = 16645480, upload-time = "2026-03-29T13:19:03.63Z" }, + { url = "https://files.pythonhosted.org/packages/34/49/f2312c154b82a286758ee2f1743336d50651f8b5195db18cdb63675ff649/numpy-2.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:62d6b0f03b694173f9fcb1fb317f7222fd0b0b103e784c6549f5e53a27718c44", size = 17020036, upload-time = "2026-03-29T13:19:07.428Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e9/736d17bd77f1b0ec4f9901aaec129c00d59f5d84d5e79bba540ef12c2330/numpy-2.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc356aae7adf9e6336d336b9c8111d390a05df88f1805573ebb0807bd06fd1d", size = 18368643, upload-time = "2026-03-29T13:19:10.775Z" }, + { url = "https://files.pythonhosted.org/packages/63/f6/d417977c5f519b17c8a5c3bc9e8304b0908b0e21136fe43bf628a1343914/numpy-2.4.4-cp312-cp312-win32.whl", hash = "sha256:0d35aea54ad1d420c812bfa0385c71cd7cc5bcf7c65fed95fc2cd02fe8c79827", size = 5961117, upload-time = "2026-03-29T13:19:13.464Z" }, + { url = "https://files.pythonhosted.org/packages/2d/5b/e1deebf88ff431b01b7406ca3583ab2bbb90972bbe1c568732e49c844f7e/numpy-2.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:b5f0362dc928a6ecd9db58868fca5e48485205e3855957bdedea308f8672ea4a", size = 12320584, upload-time = "2026-03-29T13:19:16.155Z" }, + { url = "https://files.pythonhosted.org/packages/58/89/e4e856ac82a68c3ed64486a544977d0e7bdd18b8da75b78a577ca31c4395/numpy-2.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:846300f379b5b12cc769334464656bc882e0735d27d9726568bc932fdc49d5ec", size = 10221450, upload-time = "2026-03-29T13:19:18.994Z" }, + { url = "https://files.pythonhosted.org/packages/14/1d/d0a583ce4fefcc3308806a749a536c201ed6b5ad6e1322e227ee4848979d/numpy-2.4.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:08f2e31ed5e6f04b118e49821397f12767934cfdd12a1ce86a058f91e004ee50", size = 16684933, upload-time = "2026-03-29T13:19:22.47Z" }, + { url = "https://files.pythonhosted.org/packages/c1/62/2b7a48fbb745d344742c0277f01286dead15f3f68e4f359fbfcf7b48f70f/numpy-2.4.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e823b8b6edc81e747526f70f71a9c0a07ac4e7ad13020aa736bb7c9d67196115", size = 14694532, upload-time = "2026-03-29T13:19:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/e5/87/499737bfba066b4a3bebff24a8f1c5b2dee410b209bc6668c9be692580f0/numpy-2.4.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:4a19d9dba1a76618dd86b164d608566f393f8ec6ac7c44f0cc879011c45e65af", size = 5199661, upload-time = "2026-03-29T13:19:28.31Z" }, + { url = "https://files.pythonhosted.org/packages/cd/da/464d551604320d1491bc345efed99b4b7034143a85787aab78d5691d5a0e/numpy-2.4.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:d2a8490669bfe99a233298348acc2d824d496dee0e66e31b66a6022c2ad74a5c", size = 6547539, upload-time = "2026-03-29T13:19:30.97Z" }, + { url = "https://files.pythonhosted.org/packages/7d/90/8d23e3b0dafd024bf31bdec225b3bb5c2dbfa6912f8a53b8659f21216cbf/numpy-2.4.4-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:45dbed2ab436a9e826e302fcdcbe9133f9b0006e5af7168afb8963a6520da103", size = 15668806, upload-time = "2026-03-29T13:19:33.887Z" }, + { url = "https://files.pythonhosted.org/packages/d1/73/a9d864e42a01896bb5974475438f16086be9ba1f0d19d0bb7a07427c4a8b/numpy-2.4.4-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c901b15172510173f5cb310eae652908340f8dede90fff9e3bf6c0d8dfd92f83", size = 16632682, upload-time = "2026-03-29T13:19:37.336Z" }, + { url = "https://files.pythonhosted.org/packages/34/fb/14570d65c3bde4e202a031210475ae9cde9b7686a2e7dc97ee67d2833b35/numpy-2.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:99d838547ace2c4aace6c4f76e879ddfe02bb58a80c1549928477862b7a6d6ed", size = 17019810, upload-time = "2026-03-29T13:19:40.963Z" }, + { url = "https://files.pythonhosted.org/packages/8a/77/2ba9d87081fd41f6d640c83f26fb7351e536b7ce6dd9061b6af5904e8e46/numpy-2.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0aec54fd785890ecca25a6003fd9a5aed47ad607bbac5cd64f836ad8666f4959", size = 18357394, upload-time = "2026-03-29T13:19:44.859Z" }, + { url = "https://files.pythonhosted.org/packages/a2/23/52666c9a41708b0853fa3b1a12c90da38c507a3074883823126d4e9d5b30/numpy-2.4.4-cp313-cp313-win32.whl", hash = "sha256:07077278157d02f65c43b1b26a3886bce886f95d20aabd11f87932750dfb14ed", size = 5959556, upload-time = "2026-03-29T13:19:47.661Z" }, + { url = "https://files.pythonhosted.org/packages/57/fb/48649b4971cde70d817cf97a2a2fdc0b4d8308569f1dd2f2611959d2e0cf/numpy-2.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:5c70f1cc1c4efbe316a572e2d8b9b9cc44e89b95f79ca3331553fbb63716e2bf", size = 12317311, upload-time = "2026-03-29T13:19:50.67Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d8/11490cddd564eb4de97b4579ef6bfe6a736cc07e94c1598590ae25415e01/numpy-2.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:ef4059d6e5152fa1a39f888e344c73fdc926e1b2dd58c771d67b0acfbf2aa67d", size = 10222060, upload-time = "2026-03-29T13:19:54.229Z" }, + { url = "https://files.pythonhosted.org/packages/99/5d/dab4339177a905aad3e2221c915b35202f1ec30d750dd2e5e9d9a72b804b/numpy-2.4.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4bbc7f303d125971f60ec0aaad5e12c62d0d2c925f0ab1273debd0e4ba37aba5", size = 14822302, upload-time = "2026-03-29T13:19:57.585Z" }, + { url = "https://files.pythonhosted.org/packages/eb/e4/0564a65e7d3d97562ed6f9b0fd0fb0a6f559ee444092f105938b50043876/numpy-2.4.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:4d6d57903571f86180eb98f8f0c839fa9ebbfb031356d87f1361be91e433f5b7", size = 5327407, upload-time = "2026-03-29T13:20:00.601Z" }, + { url = "https://files.pythonhosted.org/packages/29/8d/35a3a6ce5ad371afa58b4700f1c820f8f279948cca32524e0a695b0ded83/numpy-2.4.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:4636de7fd195197b7535f231b5de9e4b36d2c440b6e566d2e4e4746e6af0ca93", size = 6647631, upload-time = "2026-03-29T13:20:02.855Z" }, + { url = "https://files.pythonhosted.org/packages/f4/da/477731acbd5a58a946c736edfdabb2ac5b34c3d08d1ba1a7b437fa0884df/numpy-2.4.4-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ad2e2ef14e0b04e544ea2fa0a36463f847f113d314aa02e5b402fdf910ef309e", size = 15727691, upload-time = "2026-03-29T13:20:06.004Z" }, + { url = "https://files.pythonhosted.org/packages/e6/db/338535d9b152beabeb511579598418ba0212ce77cf9718edd70262cc4370/numpy-2.4.4-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5a285b3b96f951841799528cd1f4f01cd70e7e0204b4abebac9463eecfcf2a40", size = 16681241, upload-time = "2026-03-29T13:20:09.417Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a9/ad248e8f58beb7a0219b413c9c7d8151c5d285f7f946c3e26695bdbbe2df/numpy-2.4.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:f8474c4241bc18b750be2abea9d7a9ec84f46ef861dbacf86a4f6e043401f79e", size = 17085767, upload-time = "2026-03-29T13:20:13.126Z" }, + { url = "https://files.pythonhosted.org/packages/b5/1a/3b88ccd3694681356f70da841630e4725a7264d6a885c8d442a697e1146b/numpy-2.4.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:4e874c976154687c1f71715b034739b45c7711bec81db01914770373d125e392", size = 18403169, upload-time = "2026-03-29T13:20:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/c2/c9/fcfd5d0639222c6eac7f304829b04892ef51c96a75d479214d77e3ce6e33/numpy-2.4.4-cp313-cp313t-win32.whl", hash = "sha256:9c585a1790d5436a5374bac930dad6ed244c046ed91b2b2a3634eb2971d21008", size = 6083477, upload-time = "2026-03-29T13:20:20.195Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e3/3938a61d1c538aaec8ed6fd6323f57b0c2d2d2219512434c5c878db76553/numpy-2.4.4-cp313-cp313t-win_amd64.whl", hash = "sha256:93e15038125dc1e5345d9b5b68aa7f996ec33b98118d18c6ca0d0b7d6198b7e8", size = 12457487, upload-time = "2026-03-29T13:20:22.946Z" }, + { url = "https://files.pythonhosted.org/packages/97/6a/7e345032cc60501721ef94e0e30b60f6b0bd601f9174ebd36389a2b86d40/numpy-2.4.4-cp313-cp313t-win_arm64.whl", hash = "sha256:0dfd3f9d3adbe2920b68b5cd3d51444e13a10792ec7154cd0a2f6e74d4ab3233", size = 10292002, upload-time = "2026-03-29T13:20:25.909Z" }, + { url = "https://files.pythonhosted.org/packages/6e/06/c54062f85f673dd5c04cbe2f14c3acb8c8b95e3384869bb8cc9bff8cb9df/numpy-2.4.4-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:f169b9a863d34f5d11b8698ead99febeaa17a13ca044961aa8e2662a6c7766a0", size = 16684353, upload-time = "2026-03-29T13:20:29.504Z" }, + { url = "https://files.pythonhosted.org/packages/4c/39/8a320264a84404c74cc7e79715de85d6130fa07a0898f67fb5cd5bd79908/numpy-2.4.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2483e4584a1cb3092da4470b38866634bafb223cbcd551ee047633fd2584599a", size = 14704914, upload-time = "2026-03-29T13:20:33.547Z" }, + { url = "https://files.pythonhosted.org/packages/91/fb/287076b2614e1d1044235f50f03748f31fa287e3dbe6abeb35cdfa351eca/numpy-2.4.4-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:2d19e6e2095506d1736b7d80595e0f252d76b89f5e715c35e06e937679ea7d7a", size = 5210005, upload-time = "2026-03-29T13:20:36.45Z" }, + { url = "https://files.pythonhosted.org/packages/63/eb/fcc338595309910de6ecabfcef2419a9ce24399680bfb149421fa2df1280/numpy-2.4.4-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:6a246d5914aa1c820c9443ddcee9c02bec3e203b0c080349533fae17727dfd1b", size = 6544974, upload-time = "2026-03-29T13:20:39.014Z" }, + { url = "https://files.pythonhosted.org/packages/44/5d/e7e9044032a716cdfaa3fba27a8e874bf1c5f1912a1ddd4ed071bf8a14a6/numpy-2.4.4-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:989824e9faf85f96ec9c7761cd8d29c531ad857bfa1daa930cba85baaecf1a9a", size = 15684591, upload-time = "2026-03-29T13:20:42.146Z" }, + { url = "https://files.pythonhosted.org/packages/98/7c/21252050676612625449b4807d6b695b9ce8a7c9e1c197ee6216c8a65c7c/numpy-2.4.4-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:27a8d92cd10f1382a67d7cf4db7ce18341b66438bdd9f691d7b0e48d104c2a9d", size = 16637700, upload-time = "2026-03-29T13:20:46.204Z" }, + { url = "https://files.pythonhosted.org/packages/b1/29/56d2bbef9465db24ef25393383d761a1af4f446a1df9b8cded4fe3a5a5d7/numpy-2.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e44319a2953c738205bf3354537979eaa3998ed673395b964c1176083dd46252", size = 17035781, upload-time = "2026-03-29T13:20:50.242Z" }, + { url = "https://files.pythonhosted.org/packages/e3/2b/a35a6d7589d21f44cea7d0a98de5ddcbb3d421b2622a5c96b1edf18707c3/numpy-2.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e892aff75639bbef0d2a2cfd55535510df26ff92f63c92cd84ef8d4ba5a5557f", size = 18362959, upload-time = "2026-03-29T13:20:54.019Z" }, + { url = "https://files.pythonhosted.org/packages/64/c9/d52ec581f2390e0f5f85cbfd80fb83d965fc15e9f0e1aec2195faa142cde/numpy-2.4.4-cp314-cp314-win32.whl", hash = "sha256:1378871da56ca8943c2ba674530924bb8ca40cd228358a3b5f302ad60cf875fc", size = 6008768, upload-time = "2026-03-29T13:20:56.912Z" }, + { url = "https://files.pythonhosted.org/packages/fa/22/4cc31a62a6c7b74a8730e31a4274c5dc80e005751e277a2ce38e675e4923/numpy-2.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:715d1c092715954784bc79e1174fc2a90093dc4dc84ea15eb14dad8abdcdeb74", size = 12449181, upload-time = "2026-03-29T13:20:59.548Z" }, + { url = "https://files.pythonhosted.org/packages/70/2e/14cda6f4d8e396c612d1bf97f22958e92148801d7e4f110cabebdc0eef4b/numpy-2.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:2c194dd721e54ecad9ad387c1d35e63dce5c4450c6dc7dd5611283dda239aabb", size = 10496035, upload-time = "2026-03-29T13:21:02.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e8/8fed8c8d848d7ecea092dc3469643f9d10bc3a134a815a3b033da1d2039b/numpy-2.4.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2aa0613a5177c264ff5921051a5719d20095ea586ca88cc802c5c218d1c67d3e", size = 14824958, upload-time = "2026-03-29T13:21:05.671Z" }, + { url = "https://files.pythonhosted.org/packages/05/1a/d8007a5138c179c2bf33ef44503e83d70434d2642877ee8fbb230e7c0548/numpy-2.4.4-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:42c16925aa5a02362f986765f9ebabf20de75cdefdca827d14315c568dcab113", size = 5330020, upload-time = "2026-03-29T13:21:08.635Z" }, + { url = "https://files.pythonhosted.org/packages/99/64/ffb99ac6ae93faf117bcbd5c7ba48a7f45364a33e8e458545d3633615dda/numpy-2.4.4-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:874f200b2a981c647340f841730fc3a2b54c9d940566a3c4149099591e2c4c3d", size = 6650758, upload-time = "2026-03-29T13:21:10.949Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6e/795cc078b78a384052e73b2f6281ff7a700e9bf53bcce2ee579d4f6dd879/numpy-2.4.4-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c9b39d38a9bd2ae1becd7eac1303d031c5c110ad31f2b319c6e7d98b135c934d", size = 15729948, upload-time = "2026-03-29T13:21:14.047Z" }, + { url = "https://files.pythonhosted.org/packages/5f/86/2acbda8cc2af5f3d7bfc791192863b9e3e19674da7b5e533fded124d1299/numpy-2.4.4-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b268594bccac7d7cf5844c7732e3f20c50921d94e36d7ec9b79e9857694b1b2f", size = 16679325, upload-time = "2026-03-29T13:21:17.561Z" }, + { url = "https://files.pythonhosted.org/packages/bc/59/cafd83018f4aa55e0ac6fa92aa066c0a1877b77a615ceff1711c260ffae8/numpy-2.4.4-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:ac6b31e35612a26483e20750126d30d0941f949426974cace8e6b5c58a3657b0", size = 17084883, upload-time = "2026-03-29T13:21:21.106Z" }, + { url = "https://files.pythonhosted.org/packages/f0/85/a42548db84e65ece46ab2caea3d3f78b416a47af387fcbb47ec28e660dc2/numpy-2.4.4-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8e3ed142f2728df44263aaf5fb1f5b0b99f4070c553a0d7f033be65338329150", size = 18403474, upload-time = "2026-03-29T13:21:24.828Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ad/483d9e262f4b831000062e5d8a45e342166ec8aaa1195264982bca267e62/numpy-2.4.4-cp314-cp314t-win32.whl", hash = "sha256:dddbbd259598d7240b18c9d87c56a9d2fb3b02fe266f49a7c101532e78c1d871", size = 6155500, upload-time = "2026-03-29T13:21:28.205Z" }, + { url = "https://files.pythonhosted.org/packages/c7/03/2fc4e14c7bd4ff2964b74ba90ecb8552540b6315f201df70f137faa5c589/numpy-2.4.4-cp314-cp314t-win_amd64.whl", hash = "sha256:a7164afb23be6e37ad90b2f10426149fd75aee07ca55653d2aa41e66c4ef697e", size = 12637755, upload-time = "2026-03-29T13:21:31.107Z" }, + { url = "https://files.pythonhosted.org/packages/58/78/548fb8e07b1a341746bfbecb32f2c268470f45fa028aacdbd10d9bc73aab/numpy-2.4.4-cp314-cp314t-win_arm64.whl", hash = "sha256:ba203255017337d39f89bdd58417f03c4426f12beed0440cfd933cb15f8669c7", size = 10566643, upload-time = "2026-03-29T13:21:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/6b/33/8fae8f964a4f63ed528264ddf25d2b683d0b663e3cba26961eb838a7c1bd/numpy-2.4.4-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:58c8b5929fcb8287cbd6f0a3fae19c6e03a5c48402ae792962ac465224a629a4", size = 16854491, upload-time = "2026-03-29T13:21:38.03Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d0/1aabee441380b981cf8cdda3ae7a46aa827d1b5a8cce84d14598bc94d6d9/numpy-2.4.4-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:eea7ac5d2dce4189771cedb559c738a71512768210dc4e4753b107a2048b3d0e", size = 14895830, upload-time = "2026-03-29T13:21:41.509Z" }, + { url = "https://files.pythonhosted.org/packages/a5/b8/aafb0d1065416894fccf4df6b49ef22b8db045187949545bced89c034b8e/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:51fc224f7ca4d92656d5a5eb315f12eb5fe2c97a66249aa7b5f562528a3be38c", size = 5400927, upload-time = "2026-03-29T13:21:44.747Z" }, + { url = "https://files.pythonhosted.org/packages/d6/77/063baa20b08b431038c7f9ff5435540c7b7265c78cf56012a483019ca72d/numpy-2.4.4-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:28a650663f7314afc3e6ec620f44f333c386aad9f6fc472030865dc0ebb26ee3", size = 6715557, upload-time = "2026-03-29T13:21:47.406Z" }, + { url = "https://files.pythonhosted.org/packages/c7/a8/379542d45a14f149444c5c4c4e7714707239ce9cc1de8c2803958889da14/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:19710a9ca9992d7174e9c52f643d4272dcd1558c5f7af7f6f8190f633bd651a7", size = 15804253, upload-time = "2026-03-29T13:21:50.753Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/f0a45426d6d21e7ea3310a15cf90c43a14d9232c31a837702dba437f3373/numpy-2.4.4-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b2aec6af35c113b05695ebb5749a787acd63cafc83086a05771d1e1cd1e555f", size = 16753552, upload-time = "2026-03-29T13:21:54.344Z" }, + { url = "https://files.pythonhosted.org/packages/04/74/f4c001f4714c3ad9ce037e18cf2b9c64871a84951eaa0baf683a9ca9301c/numpy-2.4.4-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:f2cf083b324a467e1ab358c105f6cad5ea950f50524668a80c486ff1db24e119", size = 12509075, upload-time = "2026-03-29T13:21:57.644Z" }, +] + +[[package]] +name = "opencv-python" +version = "4.13.0.92" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/6f/5a28fef4c4a382be06afe3938c64cc168223016fa520c5abaf37e8862aa5/opencv_python-4.13.0.92-cp37-abi3-macosx_13_0_arm64.whl", hash = "sha256:caf60c071ec391ba51ed00a4a920f996d0b64e3e46068aac1f646b5de0326a19", size = 46247052, upload-time = "2026-02-05T07:01:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/08/ac/6c98c44c650b8114a0fb901691351cfb3956d502e8e9b5cd27f4ee7fbf2f/opencv_python-4.13.0.92-cp37-abi3-macosx_14_0_x86_64.whl", hash = "sha256:5868a8c028a0b37561579bfb8ac1875babdc69546d236249fff296a8c010ccf9", size = 32568781, upload-time = "2026-02-05T07:01:41.379Z" }, + { url = "https://files.pythonhosted.org/packages/3e/51/82fed528b45173bf629fa44effb76dff8bc9f4eeaee759038362dfa60237/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bc2596e68f972ca452d80f444bc404e08807d021fbba40df26b61b18e01838a", size = 47685527, upload-time = "2026-02-05T06:59:11.24Z" }, + { url = "https://files.pythonhosted.org/packages/db/07/90b34a8e2cf9c50fe8ed25cac9011cde0676b4d9d9c973751ac7616223a2/opencv_python-4.13.0.92-cp37-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:402033cddf9d294693094de5ef532339f14ce821da3ad7df7c9f6e8316da32cf", size = 70460872, upload-time = "2026-02-05T06:59:19.162Z" }, + { url = "https://files.pythonhosted.org/packages/02/6d/7a9cc719b3eaf4377b9c2e3edeb7ed3a81de41f96421510c0a169ca3cfd4/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:bccaabf9eb7f897ca61880ce2869dcd9b25b72129c28478e7f2a5e8dee945616", size = 46708208, upload-time = "2026-02-05T06:59:15.419Z" }, + { url = "https://files.pythonhosted.org/packages/fd/55/b3b49a1b97aabcfbbd6c7326df9cb0b6fa0c0aefa8e89d500939e04aa229/opencv_python-4.13.0.92-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:620d602b8f7d8b8dab5f4b99c6eb353e78d3fb8b0f53db1bd258bb1aa001c1d5", size = 72927042, upload-time = "2026-02-05T06:59:23.389Z" }, + { url = "https://files.pythonhosted.org/packages/fb/17/de5458312bcb07ddf434d7bfcb24bb52c59635ad58c6e7c751b48949b009/opencv_python-4.13.0.92-cp37-abi3-win32.whl", hash = "sha256:372fe164a3148ac1ca51e5f3ad0541a4a276452273f503441d718fab9c5e5f59", size = 30932638, upload-time = "2026-02-05T07:02:14.98Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a5/1be1516390333ff9be3a9cb648c9f33df79d5096e5884b5df71a588af463/opencv_python-4.13.0.92-cp37-abi3-win_amd64.whl", hash = "sha256:423d934c9fafb91aad38edf26efb46da91ffbc05f3f59c4b0c72e699720706f5", size = 40212062, upload-time = "2026-02-05T07:02:12.724Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, + { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, + { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, + { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "propcache" +version = "0.5.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/44/c87281c333769159c50594f22610f77398a47ccbfbbf23074e744e86f87c/propcache-0.5.2.tar.gz", hash = "sha256:01c4fc7480cd0598bb4b57022df55b9ca296da7fc5a8760bd8451a7e63a7d427", size = 50208, upload-time = "2026-05-08T21:02:12.199Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/56/030b7b4719d53085722893e0009dffb9236aa10bca1b12121bdc5626ef16/propcache-0.5.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d5a81be28596d6559f6131ef33e10200de6e17643b3c74ce03f9eb103be6ae8b", size = 93417, upload-time = "2026-05-08T20:59:15.597Z" }, + { url = "https://files.pythonhosted.org/packages/1a/55/1140a8e067b8ec093a18a4ae7bb0045d9db65da38a08618ddc5e2f1994aa/propcache-0.5.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29cbaac5ea0212663e6845e04b5e188d5a6ae6dd919810ac835bf1d3b42c3f4c", size = 53847, upload-time = "2026-05-08T20:59:17.096Z" }, + { url = "https://files.pythonhosted.org/packages/20/42/0e7443c90310498561addf346e7d57fe3c6ba1914e1ba938b5464c7bbfd2/propcache-0.5.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:6bf3be92233808fcd338eba0fb4d0b59ec5772af4f4ecfcec450d1bfc0f8b5eb", size = 53512, upload-time = "2026-05-08T20:59:18.64Z" }, + { url = "https://files.pythonhosted.org/packages/b7/db/cf51a71bab2009517d1a7f0ee07657e3bd446c4d69f67e6966cf17bcf956/propcache-0.5.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2f8ea531c794b9d6274acd4e8d2c2ebcac590a4361d27482edd3010b79f1325e", size = 58068, upload-time = "2026-05-08T20:59:20.683Z" }, + { url = "https://files.pythonhosted.org/packages/b7/43/39b6bdee9699fa1e1641c519feeb64a67e2a9f93bb465c70776b37a7333f/propcache-0.5.2-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:decfca4c79dd53ebab484b00cc4b6717d8c369f86e74aa4ca395a64ac651495e", size = 61020, upload-time = "2026-05-08T20:59:22.112Z" }, + { url = "https://files.pythonhosted.org/packages/26/0b/843726fbb0a29a8c5684fdb25971823638399f31e52e9d1f06a02dc9aa6b/propcache-0.5.2-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4621064bbf28fa77ff64dd5d94367c04684c67d3a5bf1dff25f0cd0d98a38f3b", size = 62732, upload-time = "2026-05-08T20:59:23.805Z" }, + { url = "https://files.pythonhosted.org/packages/39/6e/899fed76dc1942b8a64193a4f059d7f1a2c7ef65085e8a9366ed8ec0d199/propcache-0.5.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b96db7141a592cbc968daf1feea83a118e6ab378af4abbc72b248c895414c22d", size = 60140, upload-time = "2026-05-08T20:59:25.389Z" }, + { url = "https://files.pythonhosted.org/packages/ab/09/3da4be9b5b879219ad234aa535b3dd4a080ed1ad48d3a73ca07a9e798f22/propcache-0.5.2-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1ca071adabaab6e9219924bbe00af821f1ee7de113a9eca1cdc292de3d120f4d", size = 60400, upload-time = "2026-05-08T20:59:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/60/2f/09b72b874a9aa0044faf52a69807a6ed618e267ceaa9ec4a63195fa5b504/propcache-0.5.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e4294d04a94dcab1b3bccd8b66d962dcad411a1d19414b2a41d1445f1de32ad0", size = 58155, upload-time = "2026-05-08T20:59:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/8a/37/97489848c54c95578045473954f10956d619ce6a09e7ac137b71cdcb698b/propcache-0.5.2-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:a0e399a2eccb91ed18721f86aa85757727400b6865c89e88934781deb9c8498b", size = 57037, upload-time = "2026-05-08T20:59:30.146Z" }, + { url = "https://files.pythonhosted.org/packages/22/db/6c695285ccfc49012743ee9c98212b8c5dd0aed7b63cfd816d4a0f7a1601/propcache-0.5.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:823581fd5cb08b12a48bfa11fe962a7916766b6170c17b028fbdf762b85eb9bf", size = 61103, upload-time = "2026-05-08T20:59:31.626Z" }, + { url = "https://files.pythonhosted.org/packages/98/a9/1e500401ca593b0bdb6bf75a70bc2d723835fd53360edff6af70692c7546/propcache-0.5.2-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:949c91d1a990cf3b2e8188dfcfb25005e0b834a06c63fa4ef9f360878ce21ecf", size = 60394, upload-time = "2026-05-08T20:59:32.829Z" }, + { url = "https://files.pythonhosted.org/packages/1f/87/f638b6e375eae0f30a1a2325d8b34fd85fdc785bb9960cf805f3bf1ec69a/propcache-0.5.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:cc1177027eda740fdb152706bd215a3f124e3eea15afc39f2cb9fe351b50619e", size = 63084, upload-time = "2026-05-08T20:59:35.964Z" }, + { url = "https://files.pythonhosted.org/packages/f6/18/884573f5d97b6d9eba68de759a82c901b7e39d7904d30f7b8d58d42d2a12/propcache-0.5.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b05d643f944a8c3c4bd86d65ffd87bf3264b617f87791940302bc474d2ff5274", size = 60999, upload-time = "2026-05-08T20:59:38.481Z" }, + { url = "https://files.pythonhosted.org/packages/8f/1a/c3915eb059ceec9e758a56e4cfd955292bc0f201be2176a46b76d94b303a/propcache-0.5.2-cp310-cp310-win32.whl", hash = "sha256:8114f28879e0904748e831c3a7774261bd9e75f49be089f389a76f959dcd13fe", size = 39036, upload-time = "2026-05-08T20:59:40.323Z" }, + { url = "https://files.pythonhosted.org/packages/5b/02/1dfd5607501a602d19c1c449d2d193b7d1c611f9246b4059026a1189a80e/propcache-0.5.2-cp310-cp310-win_amd64.whl", hash = "sha256:5fcb98e7598b1ee0addab320d90f65b530297a867dbfe9de52ea838077e16e3d", size = 42190, upload-time = "2026-05-08T20:59:42.232Z" }, + { url = "https://files.pythonhosted.org/packages/57/93/f71588ad08b3e6f4b555b5ef215808a3c02b042d0151ad82fa6f15be677a/propcache-0.5.2-cp310-cp310-win_arm64.whl", hash = "sha256:04dc2390d9edbbaef7461f33322555976ffddf0b650a038649d026358714e6c5", size = 38545, upload-time = "2026-05-08T20:59:44.087Z" }, + { url = "https://files.pythonhosted.org/packages/e7/f1/8a8cc1c2c7e7934ab77e0163414f736fadbc0f5e8dd9673b952355ac175b/propcache-0.5.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:74b70780220e2dd89175ca24b81b68b67c83db499ae611e7f2313cb329801c78", size = 90744, upload-time = "2026-05-08T20:59:45.799Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f4/651b1225e976bd1a2ba5cfba0c29d096581c2636b437e3a9a7ab6276270a/propcache-0.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a4840ab0ae0216d952f4b53dc6d0b992bfc2bedbfe360bdd9b548bc184c08959", size = 52033, upload-time = "2026-05-08T20:59:47.408Z" }, + { url = "https://files.pythonhosted.org/packages/15/a8/8ede85d6aa1f79fc7dc2f8fd2c8d65920b8272c3892903c8a1affde48cfb/propcache-0.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c6844ba6364fb12f403928a82cfd295ab103a2b315c77c747b2dbe4a41894ea7", size = 52754, upload-time = "2026-05-08T20:59:49.202Z" }, + { url = "https://files.pythonhosted.org/packages/7d/fe/b3551b41bbc2f5b5bb088fc6920567cd43101253e68fbaa261339eb96fe1/propcache-0.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2293949b855ce597f2826452d17c2d545fb5622379c4ea6fdf525e9b8e8a2511", size = 57573, upload-time = "2026-05-08T20:59:50.778Z" }, + { url = "https://files.pythonhosted.org/packages/83/27/ab851ebd1b7172e3e161f5f8d39e315d54a91bea246f01f4d872d3376aef/propcache-0.5.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0fd59b5af35f74da48d905dcbad55449ba13be91823cb05a9bd590bbf5b61660", size = 60645, upload-time = "2026-05-08T20:59:52.227Z" }, + { url = "https://files.pythonhosted.org/packages/95/7d/466b3d18022e9897cbda9c735c493c5bd747d7a4c6f5ea1480b4cec434b6/propcache-0.5.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29f9309a2e42b0d273be006fdb4be2d6c39a47f6f57d8fb1cf9f81481df81b66", size = 61563, upload-time = "2026-05-08T20:59:53.866Z" }, + { url = "https://files.pythonhosted.org/packages/27/1b/16ab7f2cf2041da2f60d156ba64c2484eadf9168075b4ff43c3ef60045af/propcache-0.5.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5aaa2b923c1944ac8febd6609cb373540a5563e7cbcb0fd770f75dace2eb817b", size = 58888, upload-time = "2026-05-08T20:59:55.457Z" }, + { url = "https://files.pythonhosted.org/packages/0a/67/bb777ffd907633563bf35fd859c4ce97b0512c32f4633cf5d1eb7c33512b/propcache-0.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:66ea454f095ddf5b6b14f56c064c0941c4788be11e18d2464cf643bf7203ff67", size = 59253, upload-time = "2026-05-08T20:59:57.075Z" }, + { url = "https://files.pythonhosted.org/packages/b9/42/64f8d90b73fd9cdc1499b48057ff6d9cd2a98a25734c9bb62ecf07e87061/propcache-0.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:95f1e3f4760d404b13c9976c0229b2b49a3c8e2c62a9ce92efdd2b11ada75e3f", size = 57558, upload-time = "2026-05-08T20:59:58.602Z" }, + { url = "https://files.pythonhosted.org/packages/eb/02/dba5bc03c9041f2092ea55a449caf5dfe68352c6654511b29ba0654ddb69/propcache-0.5.2-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:85341b12b9d55bad0bded24cac341bb34289469e03a11f3f583ea1cc1db0326c", size = 55007, upload-time = "2026-05-08T20:59:59.837Z" }, + { url = "https://files.pythonhosted.org/packages/14/c0/43f649c7aa2a77a3b100d84e9dea3a483120ecb608bfe36ce49eaff517fe/propcache-0.5.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:26a4dca084132874e639895c3135dfad5eb20bae209f62d1aeb31b03e601c3c0", size = 60355, upload-time = "2026-05-08T21:00:01.144Z" }, + { url = "https://files.pythonhosted.org/packages/83/c0/435dafd27f1cb4a495381dae60e25883ccfe4020bb72818e8184c1678092/propcache-0.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3b199b9b2b3d6a7edf3183ba8a9a137a22b97f7df525feb5ae1eccf026d2a9c6", size = 59057, upload-time = "2026-05-08T21:00:02.401Z" }, + { url = "https://files.pythonhosted.org/packages/53/ae/6e292df9135d659944e96cb3389258e4a663e5b2b5f6c217ef0ddc8d2f73/propcache-0.5.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e59bc9e66329185b93dab73f210f1a37f81cb40f321501db8017c9aea15dba27", size = 61938, upload-time = "2026-05-08T21:00:03.638Z" }, + { url = "https://files.pythonhosted.org/packages/0b/42/314ebc50d8159055411fd6b0bda322ff510e4b1f7d2e4927940ad0f6af20/propcache-0.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:552ffadf6ad409844bc5919c42a0a83d88314cedddaea0e41e80a8b8fffe881f", size = 59731, upload-time = "2026-05-08T21:00:04.881Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9b/2da6dee38871c3c8772fabc2758325a5c9077d6d18c597737dc04dd884cd/propcache-0.5.2-cp311-cp311-win32.whl", hash = "sha256:cd416c1de191973c52ff1a12a57446bfc7642797b282d7caf2162d7d1b8aa9a0", size = 38966, upload-time = "2026-05-08T21:00:06.511Z" }, + { url = "https://files.pythonhosted.org/packages/42/4e/f17363fb58c0afe05b067361cb6d86ed2d29de6506779a27547c4d183075/propcache-0.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:44e488ef40dbb452700b2b1f8188934121f6648f52c295055662d2191959ff82", size = 42135, upload-time = "2026-05-08T21:00:08.088Z" }, + { url = "https://files.pythonhosted.org/packages/c6/eb/6af6685077d22e8b33358d3c548e3282706a0b3cd85044ffba4e5dd08e3b/propcache-0.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:54adaa85a22078d1e306304a40984dc5be99d599bf3dc0a24dc98f7daeab89ab", size = 38381, upload-time = "2026-05-08T21:00:09.692Z" }, + { url = "https://files.pythonhosted.org/packages/4a/cb/e27bc2b2737a0bb49962b275efa051e8f1c35a936df7d5139b6b658b7dc9/propcache-0.5.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:806719138ecd720339a12410fb9614ac9b2b2d3a5fdf8235d56981c36f4039ba", size = 95887, upload-time = "2026-05-08T21:00:11.277Z" }, + { url = "https://files.pythonhosted.org/packages/e6/13/b8ae04c59392f8d11c6cd9fb4011d1dc7c86b81225c770280300e259ffe1/propcache-0.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:db2b80ea58eab4f86b2beec3cc8b39e8ff9276ac20e96b7cce43c8ae84cd6b5a", size = 54654, upload-time = "2026-05-08T21:00:12.604Z" }, + { url = "https://files.pythonhosted.org/packages/2c/7d/49777a3e20b55863d4794384a38acd460c04157b0a00f8602b0d508b8431/propcache-0.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e5cbfac9f61484f7e9f3597775500cd3ebe8274e9b050c38f9525c77c97520bf", size = 55190, upload-time = "2026-05-08T21:00:13.935Z" }, + { url = "https://files.pythonhosted.org/packages/44/c7/085d0cd63062e84044e3f05797749c3f8e3938ff3aeb0eb2f69d43fafc91/propcache-0.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5dbc581d2814337da56222fab8dc5f161cd798a434e49bac27930aaef798e144", size = 59995, upload-time = "2026-05-08T21:00:15.526Z" }, + { url = "https://files.pythonhosted.org/packages/9c/42/32cf8e3009e92b2645cf1e944f701e8ea4e924dffde1ee26db860bcbf7e4/propcache-0.5.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:857187f381f88c8e2fa2fe56ab94879d011b883d5a2ee5a1b60a8cd2a06846d9", size = 63422, upload-time = "2026-05-08T21:00:16.824Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f112433f99fc979431b87a39ef169e3f8df070d99a72792c56d6937ac48b/propcache-0.5.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:178b4a2cdaac1818e2bf1c5a99b94383fa73ea5382e032a48dec07dc5668dc42", size = 64342, upload-time = "2026-05-08T21:00:18.362Z" }, + { url = "https://files.pythonhosted.org/packages/14/15/5574111ae50dd6e879456888c0eadd4c5a869959775854e18e18a6b345f3/propcache-0.5.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6f328175a2cde1f0ff2c4ed8ce968b9dcfb55f3a7153f39e2957ed994da13476", size = 61639, upload-time = "2026-05-08T21:00:19.692Z" }, + { url = "https://files.pythonhosted.org/packages/cc/da/4d775080b1490c0ae604acda868bd71aabe3a89ed16f2aa4339eb8a283e7/propcache-0.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5671d09a36b06d0fd4a3da0fccbcae360e9b1570924171a15e9e0997f0249fba", size = 61588, upload-time = "2026-05-08T21:00:21.155Z" }, + { url = "https://files.pythonhosted.org/packages/04/ac/f076982cbe2195ee9cf32de5a1e46951d9fb399fc207f390562dd0fd8fb2/propcache-0.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:80168e2ebe4d3ec6599d10ad8f520304ae1cad9b6c5a95372aef1b66b7bfb53a", size = 60029, upload-time = "2026-05-08T21:00:22.713Z" }, + { url = "https://files.pythonhosted.org/packages/70/60/189be62e0dd898dce3b331e1b8c7a543cd3a405ac0c81fe8ee8a9d5d77e1/propcache-0.5.2-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:45f11346f884bc47444f6e6647131055844134c3175b629f84952e2b5cd62b64", size = 56774, upload-time = "2026-05-08T21:00:24.001Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/93377b9c7939c1ffae98f878dee955efadfd638078bc86dbc21f9d52f651/propcache-0.5.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:8e778ebd44ef4f66ed60a0416b06b489687db264a9c0b3620362f26489492913", size = 63532, upload-time = "2026-05-08T21:00:25.545Z" }, + { url = "https://files.pythonhosted.org/packages/14/f9/590ef6cfb9b8028d516d287812ece32bb0bc5f11fbb9c8bf6b2e6313fec8/propcache-0.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c0cb9ed24c8964e172768d455a38254c2dd8a552905729ce006cad3d3dda59b1", size = 61592, upload-time = "2026-05-08T21:00:27.186Z" }, + { url = "https://files.pythonhosted.org/packages/b4/5e/70958b3034c297a630bba2f17ca7abc2d5f39a803ad7e370ab79d1ecd022/propcache-0.5.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:1d1ad32d9d4355e2be65574fd0bfd3677e7066b009cd5b9b2dee8aa6a6393b33", size = 64788, upload-time = "2026-05-08T21:00:28.8Z" }, + { url = "https://files.pythonhosted.org/packages/12/fd/77fe5936d8c3086ca9048f7f415f122ed82e53884a9ec193646b42deef06/propcache-0.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c80f4ba3e8f00189165999a742ee526ebeccedf6c3f7beb0c7df821e9772435a", size = 62514, upload-time = "2026-05-08T21:00:30.098Z" }, + { url = "https://files.pythonhosted.org/packages/cf/74/66bd798b5b3be70aa1b391f5cc9d6a0a5532d7fd3b19ec0b213e72e6ad9d/propcache-0.5.2-cp312-cp312-win32.whl", hash = "sha256:8c7972d8f193740d9175f0998ab38717e6cd322d5935c5b0fef8c0d323fd9031", size = 39018, upload-time = "2026-05-08T21:00:31.622Z" }, + { url = "https://files.pythonhosted.org/packages/61/7c/5c0d34aa3024694d6dcb9271cdbdd08c4e47c1c0ad95ec7e7bc74cdea145/propcache-0.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:d9ee8826a7d47863a08ac44e1a5f611a462eefc3a194b492da242128bec75b42", size = 42322, upload-time = "2026-05-08T21:00:32.918Z" }, + { url = "https://files.pythonhosted.org/packages/4d/91/875812f1a3feb20ceba818ef39fbe4d92f1081e04ac815c822496d0d038b/propcache-0.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:2800a4a8ead6b28cccd1ec54b59346f0def7922ee1c7598e8499c733cfbb7c84", size = 38172, upload-time = "2026-05-08T21:00:35.124Z" }, + { url = "https://files.pythonhosted.org/packages/c5/09/f049e45385503fe67db75a6b6186a7b9f0c3930366dc960522c312a825b1/propcache-0.5.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:099aaf4b4d1a02265b92a977edf00b5c4f63b3b17ac6de39b0d637c9cac0188a", size = 94457, upload-time = "2026-05-08T21:00:36.355Z" }, + { url = "https://files.pythonhosted.org/packages/6b/65/83d1d05655baf63113731bd5a1008435e14f8d1e5a06cbe4ec5b23ad7a31/propcache-0.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:68ce1c44c7a813a7f71ea04315a8c7b330b63db99d059a797a4651bb6f69f117", size = 53835, upload-time = "2026-05-08T21:00:38.072Z" }, + { url = "https://files.pythonhosted.org/packages/a9/12/a6ba6482bb5ea3260c000c9b20881c95fa11c6b30173715668259f844ed7/propcache-0.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:fc299c129490f55f254cd90be0deca4764e36e9a7c08b4aa588479a3bbed3098", size = 54545, upload-time = "2026-05-08T21:00:39.319Z" }, + { url = "https://files.pythonhosted.org/packages/a9/19/7fa086f5764c59ec8a8e157cd93aa8497acc00aba9dcdec56bfffb32602d/propcache-0.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6ae2198be502c10f09b2516e7b5d019816924bc3183a43ce792a7bd6625e6f4", size = 59886, upload-time = "2026-05-08T21:00:40.621Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e4/5d7663dc8235956c8f5281698a3af1d351d8820341ddd890f59d9a9127f2/propcache-0.5.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6041d31504dc1779d700e1edcfb08eea334b357620b06681a4eabb57a74e574e", size = 63261, upload-time = "2026-05-08T21:00:41.775Z" }, + { url = "https://files.pythonhosted.org/packages/4a/4a/15a03adee24d6350da4292caeac44c34c033d2afe5e87eb370f38854560f/propcache-0.5.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f7eabc04151c78a9f4d5bbb5f1faf571e4defeb4b585e0fe95b60ff2dbe4d3d7", size = 64184, upload-time = "2026-05-08T21:00:43.018Z" }, + { url = "https://files.pythonhosted.org/packages/8b/c6/979176efdaa3d239e36d503d5af63a0a773b36662ed8f52e5b6a6d9fd40e/propcache-0.5.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4db0ba63d693afd40d249bd93f842b5f144f8fcbb83de05660373bcf30517b1d", size = 61534, upload-time = "2026-05-08T21:00:44.507Z" }, + { url = "https://files.pythonhosted.org/packages/c8/22/63e8cd1bae4c2d2be6493b6b7d10566ddafad88137cfbc99964a1119853c/propcache-0.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1dbcf7675229b35d31abb6547d8ebc8c27a830ac3f9a794edff6254873ec7c0a", size = 61500, upload-time = "2026-05-08T21:00:45.796Z" }, + { url = "https://files.pythonhosted.org/packages/60/5a/28e5d9acbac1cc9ccb67045e8c1b943aa8d79fdf39c93bd73cacd68008ea/propcache-0.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d310c013aad2c72f1c3f2f8dd3279d460a858c551f97aeb8c63e4693cca7b4d2", size = 59994, upload-time = "2026-05-08T21:00:47.093Z" }, + { url = "https://files.pythonhosted.org/packages/f3/40/db650677f554a95b9c01a7c9d93d629e93a15562f5deb4573c9ee136fed2/propcache-0.5.2-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:06187263ddad280d05b4d8a8b3bb7d164cbebd469236544a42e6d9b28ac6a4fa", size = 56884, upload-time = "2026-05-08T21:00:48.376Z" }, + { url = "https://files.pythonhosted.org/packages/80/45/70b39b89516ff8b96bf732fa6fded8cef20f293cb1508690101c3c07ec51/propcache-0.5.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:3115559b8effafd63b142ea5ed53d63a16ea6469cbc63dce4ee194b42db5d853", size = 63464, upload-time = "2026-05-08T21:00:49.954Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e2/fa59d3a89eac5534293124af4f1d0d0ada091ce4a0ab4610ce03fd2bdd8d/propcache-0.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c60462af8e6dc30c35407c7237ea908d777b22862bbee27bc4699c0d8bcdc45a", size = 61588, upload-time = "2026-05-08T21:00:51.281Z" }, + { url = "https://files.pythonhosted.org/packages/0b/97/efb547a55c4bc7381cfb202d6a2239ac621045277bc1ea5dfd3a7f0516c0/propcache-0.5.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:40314bca9ac559716fe374094fc81c11dcc34b64fd6c585360f5775690505704", size = 64667, upload-time = "2026-05-08T21:00:52.602Z" }, + { url = "https://files.pythonhosted.org/packages/92/56/f5c7d9b4b7595d5127da38974d791b2153f3d1eae6c674af3583ace92ad3/propcache-0.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cfa21e036ce1e1db2be04ba3b85d2df1bb1702fa01932d984c5464c665228ff4", size = 62463, upload-time = "2026-05-08T21:00:54.303Z" }, + { url = "https://files.pythonhosted.org/packages/bd/3b/484a3a65fc9f9f60c41dcd17b428bace5389544e2c680994534a20755066/propcache-0.5.2-cp313-cp313-win32.whl", hash = "sha256:f156a3529f38063b6dbaf356e15602a7f95f8055b1295a438433a6386f10463d", size = 38621, upload-time = "2026-05-08T21:00:55.808Z" }, + { url = "https://files.pythonhosted.org/packages/1c/fd/3f0f10dba4dabad3bf53102be007abf55481067952bde0fdddff439e7c61/propcache-0.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:dfed59d0a5aeb01e242e66ff0300bc4a265a7c05f612d30016f0b60b1017d757", size = 41649, upload-time = "2026-05-08T21:00:57.061Z" }, + { url = "https://files.pythonhosted.org/packages/90/ec/6ce619cc32bb500a482f811f9cd509368b4e58e638d13f2c68f370d6b475/propcache-0.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:ba338430e87ceb9c8f0cf754de38a9860560261e56c00376debd628698a7364f", size = 37636, upload-time = "2026-05-08T21:00:58.646Z" }, + { url = "https://files.pythonhosted.org/packages/1b/82/c1d268bbbf2ef981c5bf0fbbe746db617c66e3bcefe431a1aa8943fbe23a/propcache-0.5.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:a592f5f3da71c8691c788c13cb6734b6d17663d2e1cb8caddf0673d01ef8847d", size = 98872, upload-time = "2026-05-08T21:00:59.889Z" }, + { url = "https://files.pythonhosted.org/packages/f4/d4/52c871e73e864e6b34c0e2d58ac1ec5ccd149497ddc7ad2137ae98323a35/propcache-0.5.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:6a997d0489e9668a384fcfd5061b857aa5361de73191cac204d04b889cfbbafa", size = 56257, upload-time = "2026-05-08T21:01:01.195Z" }, + { url = "https://files.pythonhosted.org/packages/67/f0/9b90ca2a210b3d09bcfcd96ecd0f55545c091535abce2a45de2775cfd357/propcache-0.5.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:10734b5484ea113152ee25a91dccedf81631791805d2c9ccb054958e51842c94", size = 56696, upload-time = "2026-05-08T21:01:02.941Z" }, + { url = "https://files.pythonhosted.org/packages/9d/0e/6e9d4ba07c8e56e21ddec1e75f12148142b21ca83a51871babce095334f4/propcache-0.5.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cafca7e56c12bb02ae16d283742bef25a61122e9dab2b5b3f2ccbe589ce32164", size = 62378, upload-time = "2026-05-08T21:01:04.475Z" }, + { url = "https://files.pythonhosted.org/packages/65/19/c10badaa463dde8a27ce884f8ee2ec37e6035b7c9f5ff0c8f74f06f08dac/propcache-0.5.2-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f064f8d2b59177878b7615df1735cd8fe3462ed6be8c7b217d17a276489c2b7f", size = 65283, upload-time = "2026-05-08T21:01:05.959Z" }, + { url = "https://files.pythonhosted.org/packages/b0/b6/93bea99ca80e19cef6512a8580e5b7857bbe09422d9daa7fd4ef5723306c/propcache-0.5.2-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f78abfa8dfc32376fd1aacf597b2f2fbbe0ea751419aee718af5d4f82537ef8c", size = 66616, upload-time = "2026-05-08T21:01:07.228Z" }, + { url = "https://files.pythonhosted.org/packages/83/e4/5c7462e50625f051f37fb38b8224f7639f667184bbd34424ec83819bb1b7/propcache-0.5.2-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f7467da8a9822bf1a55336f877340c5bcbd3c482afc43a99771169f74a26dedc", size = 63773, upload-time = "2026-05-08T21:01:08.514Z" }, + { url = "https://files.pythonhosted.org/packages/ca/b6/99238894047b13c823be25027e736626cd414a52a5e30d2c3347c2733529/propcache-0.5.2-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a6ddc6ac9e25de626c1f129c1b467d7ecd33ce2237d3fd0c4e429feef0a7ee1f", size = 63664, upload-time = "2026-05-08T21:01:09.874Z" }, + { url = "https://files.pythonhosted.org/packages/85/1e/a3a1a63116a2b8edb415a8bb9a6f0c34bd03830b1e18e8ce2904e1dc1cf4/propcache-0.5.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:2f22cbbac9e26a8e864c0985ff1268d5d939d53d9d9411a9824279097e03a2cb", size = 62643, upload-time = "2026-05-08T21:01:11.132Z" }, + { url = "https://files.pythonhosted.org/packages/e4/03/893cf147de2fc6543c5eaa07ad833170e7e2a2385725bbebe8c0503723bb/propcache-0.5.2-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:fc76378c62a0f04d0cd82fbb1a2cd2d7e28fcb40d5873f28a6c44e388aaa2751", size = 59595, upload-time = "2026-05-08T21:01:12.387Z" }, + { url = "https://files.pythonhosted.org/packages/86/3b/04c1a2e12c57766568ba75ba72b3bf2042818d4c1425fab6fc07155c7cff/propcache-0.5.2-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:acd2c8edba48e31e58a363b8cf4e5c7db3b04b3f9e371f601df30d9b0d244836", size = 65711, upload-time = "2026-05-08T21:01:13.676Z" }, + { url = "https://files.pythonhosted.org/packages/1c/34/80f8d0099f8d6bacc4de1624c85672681c8cd1149ca2da0e38fd120b817f/propcache-0.5.2-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:452b5065457eb9991ec5eb38ff41d6cd4c991c9ac7c531c4d5849ae473a9a13f", size = 64247, upload-time = "2026-05-08T21:01:14.936Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1a/8b08f3a5f1037e9e370c55883ceeeee0f6dd0416fb2d2d67b8bfc91f2a79/propcache-0.5.2-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:3430bb2bfe1331885c427745a751e774ee679fd4344f80b97bf879815fe8fa55", size = 67102, upload-time = "2026-05-08T21:01:16.281Z" }, + { url = "https://files.pythonhosted.org/packages/34/68/8bdb7bb7756d76e005490649d10e4a8369e610c74d619f71e1aedf889e9c/propcache-0.5.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:cef6cea3922890dd6c9654971001fa797b526c16ab5e1e46c05fd6f877be7568", size = 64964, upload-time = "2026-05-08T21:01:17.57Z" }, + { url = "https://files.pythonhosted.org/packages/0a/aa/50fb0b5d3968b61a510926ff8b8465f1d6e976b3ab74496d7a4b9fc42515/propcache-0.5.2-cp313-cp313t-win32.whl", hash = "sha256:72d61e16dd78228b58c5d47be830ff3da7e5f139abdf0aef9d86cde1c5cf2191", size = 42546, upload-time = "2026-05-08T21:01:18.946Z" }, + { url = "https://files.pythonhosted.org/packages/ae/4c/0ddbae64321bd4a95bcbfc19307238016b5b1fee645c84626c8d539e5b74/propcache-0.5.2-cp313-cp313t-win_amd64.whl", hash = "sha256:0958834041a0166d343b8d2cedcd8bcbaeb4fdbe0cf08320c5379f143c3be6e7", size = 46330, upload-time = "2026-05-08T21:01:20.162Z" }, + { url = "https://files.pythonhosted.org/packages/00/d9/9cddc8efb78d8af264c5ec9f6d10b62f57c515feda8d321595f56010fb23/propcache-0.5.2-cp313-cp313t-win_arm64.whl", hash = "sha256:6de8bd93ddde9b992cf2b2e0d796d501a19026b5b9fd87356d7d0779531a8d96", size = 40521, upload-time = "2026-05-08T21:01:21.399Z" }, + { url = "https://files.pythonhosted.org/packages/e2/ea/23ee535d90ce8bcc465a3028eb3cc0ce3bd1005f4bb27710b30587de798d/propcache-0.5.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:46088abff4cba581dea21ae0467a480526cb25aa5f3c269e909f800328bc3999", size = 94662, upload-time = "2026-05-08T21:01:22.683Z" }, + { url = "https://files.pythonhosted.org/packages/b5/06/c5a52f419b5d8972f8d46a7577476090d8e3263ff589ce40b5ca4968d5be/propcache-0.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:fc88b26f08d634f7bc819a7852e5214f5802641ab8d9fd5326892292eee1993e", size = 53928, upload-time = "2026-05-08T21:01:23.986Z" }, + { url = "https://files.pythonhosted.org/packages/63/b1/4260d67d6bd85e58a66b72d54ce15d5de789b6f3870cc6bedf8ff9667401/propcache-0.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:97797ebb098e670a2f92dd66f32897e30d7615b14e7f59711de23e30a9072539", size = 54650, upload-time = "2026-05-08T21:01:25.305Z" }, + { url = "https://files.pythonhosted.org/packages/70/06/2f46c318e3307cd7a6a7481def374ce838c0fe20084b39dd54b0879d0e99/propcache-0.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ba57fffe4ac99c5d30076161b5866336d97600769bad35cc68f7774b15298a4e", size = 59912, upload-time = "2026-05-08T21:01:26.545Z" }, + { url = "https://files.pythonhosted.org/packages/4c/29/fe1aebec2ce57ab985a9c382bded1124431f85078113aa222c5d278430d4/propcache-0.5.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:583c19759d9eec1e5b69e2fbef36a7d9c326041be9746cb822d335c8cedc2979", size = 63300, upload-time = "2026-05-08T21:01:27.937Z" }, + { url = "https://files.pythonhosted.org/packages/b4/18/2334b26768b6c82be8c69e83671b767d5ef426aa09b0cba6c2ea47816774/propcache-0.5.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d0326e2e5e1f3163fa306c834e48e8d490e5fae607a097a40c0648109b47ba80", size = 64208, upload-time = "2026-05-08T21:01:29.484Z" }, + { url = "https://files.pythonhosted.org/packages/2b/76/7f1bfd6afff4c5e38e36a3c6d68eb5f4b7311ea80baf693db78d95b603c4/propcache-0.5.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e00820e192c8dbebcafb383ebbf99030895f09905e7a0eb2e0340a0bcc2bc825", size = 61633, upload-time = "2026-05-08T21:01:31.068Z" }, + { url = "https://files.pythonhosted.org/packages/c4/46/b3ff8aba2b4953a3e50de2cf72f1b5748b8eca93b15f3dc2c84339084c09/propcache-0.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c66afea89b1e43725731d2004732a046fe6fe955d51f952c3e95a7314a284a39", size = 61724, upload-time = "2026-05-08T21:01:32.374Z" }, + { url = "https://files.pythonhosted.org/packages/c5/01/814cfcafbcff954f94c01cf30e097ddc88a076b5440fbcf4570753437d40/propcache-0.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:d4dc37dec6c6cdad0b57881a5658fd14fbf53e333b1a86cf86559f190e1d9ec4", size = 60069, upload-time = "2026-05-08T21:01:33.67Z" }, + { url = "https://files.pythonhosted.org/packages/da/68/5c6f7622d510cc666a300687e06fd060c1a43361c0c9b20d284f06d8096a/propcache-0.5.2-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:5570dbcc97571c15f68068e529c92715a12f8d54030e272d264b377e22bd17a5", size = 57099, upload-time = "2026-05-08T21:01:34.915Z" }, + { url = "https://files.pythonhosted.org/packages/55/27/9cb0b4c679124085327957d42521c99dba04c88c90c3e55a6f0b633ebccc/propcache-0.5.2-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:f814362777a9f841adddb200ecdf8f5cb1e5a3c4b7a86378edbd6ccb26edd702", size = 63391, upload-time = "2026-05-08T21:01:36.231Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9d/7258aaa5bdf60fc6f27591eef6fe52768cb0beda7140be477c8b12c9794a/propcache-0.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:196913dea116aeb5a2ba95af4ddcb7ea85559ae07d8eee8751688310d09168c3", size = 61626, upload-time = "2026-05-08T21:01:37.545Z" }, + { url = "https://files.pythonhosted.org/packages/8e/0d/41c602003e8a9b16fe1e7eadf62c7bfba9d5474370b24200bf48b315f45f/propcache-0.5.2-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:6e7b8719005dd1175be4ab1cd25e9b98659a5e0347331506ec6760d2773a7fb5", size = 64781, upload-time = "2026-05-08T21:01:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f3/38e66b1856e9bd079deea015bc4a55f7767c0e4db2f7dcf69e7e680ba4ce/propcache-0.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:51f96d685ab16e88cab128cd37a52c5da540809c8b879fa047731bfcb4ad35a4", size = 62570, upload-time = "2026-05-08T21:01:40.415Z" }, + { url = "https://files.pythonhosted.org/packages/95/ca/bbfe9b910ce57dde8bb4876b4520fc02a4e89497c10de26be936758a3aaa/propcache-0.5.2-cp314-cp314-win32.whl", hash = "sha256:cc6fc3cc62e8501d3ed62894425040d2728ecddb1ed072737a5c70bd537aa9f0", size = 39436, upload-time = "2026-05-08T21:01:41.654Z" }, + { url = "https://files.pythonhosted.org/packages/61/d2/45c9defbaa1ea297035d9d4cce9e8f80daafbf19319c6007f157c6256ea9/propcache-0.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:81e3a30b0bb60caa22033dd0f8a3618d1d67356212514f62c57db75cb0ef410c", size = 42373, upload-time = "2026-05-08T21:01:43.041Z" }, + { url = "https://files.pythonhosted.org/packages/44/68/9ea5103f41d5217d7d6ec24db90018e23aebec070c3f9a6e54d12b841fd8/propcache-0.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:0d2c9bf8528f135dbb805ce027567e09164f7efa51a2be07458a2c0420f292d0", size = 38554, upload-time = "2026-05-08T21:01:44.336Z" }, + { url = "https://files.pythonhosted.org/packages/8a/81/fadf555f42d3b762eea8a53950b0489fdc0aa9da5f8ed9e10ce0a4e01b48/propcache-0.5.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4bc8ff1feffc6a61c7002ffe84634c41b822e104990ae009f44a0834430070bb", size = 99395, upload-time = "2026-05-08T21:01:45.883Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c9/c61e134a686949cf7971af3a390148b1156f7be81c73bc0cd12c873e2d48/propcache-0.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:79aa3ff0a9b566633b642fa9caf7e21ed1c13d6feca718187873f199e1514078", size = 56653, upload-time = "2026-05-08T21:01:47.307Z" }, + { url = "https://files.pythonhosted.org/packages/cb/73/daf935ea7048ddd7ec8eec5345b4a40b619d2d178b3c0a0900796bc3c794/propcache-0.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1b31822f4474c4036bae62de9402710051d431a606d6a0f907fec79935a071aa", size = 56914, upload-time = "2026-05-08T21:01:48.573Z" }, + { url = "https://files.pythonhosted.org/packages/79/9f/aba959b435ea18617edd7cf0a7ad0b9c574b8fc7e3d2cd55fb59cb255d33/propcache-0.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:13fef48778b5a2a756523fdb781326b028ca75e32858b04f2cdd19f394564917", size = 62567, upload-time = "2026-05-08T21:01:49.903Z" }, + { url = "https://files.pythonhosted.org/packages/6c/a1/859942de9a791ff42f6141736f5b37749b8f53e65edfa49638c67dd67e6a/propcache-0.5.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8b73ab70f1a3351fbc71f663b3e645af6dd0329100c353081cf69c37433fc6fe", size = 65542, upload-time = "2026-05-08T21:01:51.204Z" }, + { url = "https://files.pythonhosted.org/packages/b5/61/315bc0fd6c0fc7f80a528b8afd209e5fc4a875ea79571b91b8f50f442907/propcache-0.5.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5538d2c13d93e4698af7e092b57bc7298fd35d1d58e656ae18f23ee0d0378e03", size = 66845, upload-time = "2026-05-08T21:01:52.539Z" }, + { url = "https://files.pythonhosted.org/packages/47/f7/9f8122e3132e8e354ac41975ef8f1099be7d5a16bc7ae562734e993665c0/propcache-0.5.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cd645f03898405cabe694fb8bc35241e3a9c332ec85627584fe3de201452b335", size = 63985, upload-time = "2026-05-08T21:01:53.847Z" }, + { url = "https://files.pythonhosted.org/packages/c8/54/c317819ec157cbf6f35df9df9657a6f82daf34d5faf15948b2f639c2192e/propcache-0.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a473b3440261e0c60706e732b2ed2f517857344fc21bf48fdfe211e2d98eb285", size = 63999, upload-time = "2026-05-08T21:01:55.179Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/387e3f7dfce0a9233df41fb888aa1c30222cb4bbbf09537c02dd9bd85fe2/propcache-0.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7afa37062e6650640e932e4cc9297d81f9f42d9944029cc386b8247dea4da837", size = 62779, upload-time = "2026-05-08T21:01:57.489Z" }, + { url = "https://files.pythonhosted.org/packages/a1/9c/596784cb5824ed61ee960d3f8655a3f0993e107c6e98ab6c818b7fb92ccb/propcache-0.5.2-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:8a90efd5777e996e42d568db9ac740b944d691e565cbfd31b2f7832f9184b2b8", size = 59796, upload-time = "2026-05-08T21:01:58.736Z" }, + { url = "https://files.pythonhosted.org/packages/c2/3d/1a6cfa1726a48542c1e8784a0761421476a5b68e09b7f36bf95eb954aaba/propcache-0.5.2-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:f19bb891234d72535764d703bfed1153cc34f4214d5bd7150aee1eec9e8f4366", size = 66023, upload-time = "2026-05-08T21:02:00.228Z" }, + { url = "https://files.pythonhosted.org/packages/e4/0e/05fd6990369477076e4e280bcb970de760fddf0161a46e988bc95f7940ec/propcache-0.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:32775082acd2d807ee3db715c7770d38767b817870acfa08c29e057f3c4d5b56", size = 64448, upload-time = "2026-05-08T21:02:01.888Z" }, + { url = "https://files.pythonhosted.org/packages/cd/86/5f8da315a4309c62c10c0b2516b17492d5d3bbe1bb862b96604db67e2a37/propcache-0.5.2-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9282fb1a3bccd038da9f768b927b24a0c753e466c086b7c4f3c6982851eefb2d", size = 67329, upload-time = "2026-05-08T21:02:03.484Z" }, + { url = "https://files.pythonhosted.org/packages/da/d3/3368efe79ab21f0cdf86ef49895811c9cc933131d4cde1f28a624e22e712/propcache-0.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc49723e2f60d6b32a0f0b08a3fd6d13203c07f1cd9566cfce0f12a917c967a2", size = 65172, upload-time = "2026-05-08T21:02:04.745Z" }, + { url = "https://files.pythonhosted.org/packages/d5/07/127e8b0bacfb325396196f9d976a22453049b89b9b2b08477cc3145faa44/propcache-0.5.2-cp314-cp314t-win32.whl", hash = "sha256:2d7aa89ebca5acc98cba9d1472d976e394782f587bad6661003602a619fd1821", size = 43813, upload-time = "2026-05-08T21:02:06.025Z" }, + { url = "https://files.pythonhosted.org/packages/88/fb/46dad6c0ae49ed230ab1b16c890c2b6314e2403e6c412976f4a72d64a527/propcache-0.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:d447bb0b3054be5818458fbb171208b1d9ff11eba14e18ca18b90cbb45767370", size = 47764, upload-time = "2026-05-08T21:02:07.353Z" }, + { url = "https://files.pythonhosted.org/packages/e7/c4/a47d0a63aa309d10d59ede6e9d4cff03a344a79d1f0f4cd0cd74997b53e0/propcache-0.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:fe67a3d11cd9b4efabfa45c3d00ffba2b26811442a73a581a94b67c2b5faccf6", size = 41140, upload-time = "2026-05-08T21:02:09.065Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ed/1cdcab6ba3d6ab7feca11fc14f0eeea80755bb53ef4e892079f31b10a25f/propcache-0.5.2-py3-none-any.whl", hash = "sha256:be1ddfcbb376e3de5d2e2db1d58d6d67463e6b4f9f040c000de8e300295465fe", size = 14036, upload-time = "2026-05-08T21:02:10.673Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/66/70/e908e9c5e52ef7c3a6c7902c9dfbb34c7e29c25d2f81ade3856445fd5c94/protobuf-6.33.6.tar.gz", hash = "sha256:a6768d25248312c297558af96a9f9c929e8c4cee0659cb07e780731095f38135", size = 444531, upload-time = "2026-03-18T19:05:00.988Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fc/9f/2f509339e89cfa6f6a4c4ff50438db9ca488dec341f7e454adad60150b00/protobuf-6.33.6-cp310-abi3-win32.whl", hash = "sha256:7d29d9b65f8afef196f8334e80d6bc1d5d4adedb449971fefd3723824e6e77d3", size = 425739, upload-time = "2026-03-18T19:04:48.373Z" }, + { url = "https://files.pythonhosted.org/packages/76/5d/683efcd4798e0030c1bab27374fd13a89f7c2515fb1f3123efdfaa5eab57/protobuf-6.33.6-cp310-abi3-win_amd64.whl", hash = "sha256:0cd27b587afca21b7cfa59a74dcbd48a50f0a6400cfb59391340ad729d91d326", size = 437089, upload-time = "2026-03-18T19:04:50.381Z" }, + { url = "https://files.pythonhosted.org/packages/5c/01/a3c3ed5cd186f39e7880f8303cc51385a198a81469d53d0fdecf1f64d929/protobuf-6.33.6-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:9720e6961b251bde64edfdab7d500725a2af5280f3f4c87e57c0208376aa8c3a", size = 427737, upload-time = "2026-03-18T19:04:51.866Z" }, + { url = "https://files.pythonhosted.org/packages/ee/90/b3c01fdec7d2f627b3a6884243ba328c1217ed2d978def5c12dc50d328a3/protobuf-6.33.6-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:e2afbae9b8e1825e3529f88d514754e094278bb95eadc0e199751cdd9a2e82a2", size = 324610, upload-time = "2026-03-18T19:04:53.096Z" }, + { url = "https://files.pythonhosted.org/packages/9b/ca/25afc144934014700c52e05103c2421997482d561f3101ff352e1292fb81/protobuf-6.33.6-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:c96c37eec15086b79762ed265d59ab204dabc53056e3443e702d2681f4b39ce3", size = 339381, upload-time = "2026-03-18T19:04:54.616Z" }, + { url = "https://files.pythonhosted.org/packages/16/92/d1e32e3e0d894fe00b15ce28ad4944ab692713f2e7f0a99787405e43533a/protobuf-6.33.6-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:e9db7e292e0ab79dd108d7f1a94fe31601ce1ee3f7b79e0692043423020b0593", size = 323436, upload-time = "2026-03-18T19:04:55.768Z" }, + { url = "https://files.pythonhosted.org/packages/0c/bd/88a687e9147329fc7e6c26a058fc52214c47190688a496bb283000a4d2a3/protobuf-6.33.6-cp39-cp39-win32.whl", hash = "sha256:bd56799fb262994b2c2faa1799693c95cc2e22c62f56fb43af311cae45d26f0e", size = 425861, upload-time = "2026-03-18T19:04:57.064Z" }, + { url = "https://files.pythonhosted.org/packages/84/d6/fab384eea064bfc3b273183e4e09bb3a3cf4ec83876b3828c09fcacbb651/protobuf-6.33.6-cp39-cp39-win_amd64.whl", hash = "sha256:f443a394af5ed23672bc6c486be138628fbe5c651ccbc536873d7da23d1868cf", size = 437109, upload-time = "2026-03-18T19:04:58.713Z" }, + { url = "https://files.pythonhosted.org/packages/c4/72/02445137af02769918a93807b2b7890047c32bfb9f90371cbc12688819eb/protobuf-6.33.6-py3-none-any.whl", hash = "sha256:77179e006c476e69bf8e8ce866640091ec42e1beb80b213c3900006ecfba6901", size = 170656, upload-time = "2026-03-18T19:04:59.826Z" }, +] + +[[package]] +name = "protobuf" +version = "7.34.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/6b/6b/a0e95cad1ad7cc3f2c6821fcab91671bd5b78bd42afb357bb4765f29bc41/protobuf-7.34.1.tar.gz", hash = "sha256:9ce42245e704cc5027be797c1db1eb93184d44d1cdd71811fb2d9b25ad541280", size = 454708, upload-time = "2026-03-20T17:34:47.036Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/11/3325d41e6ee15bf1125654301211247b042563bcc898784351252549a8ad/protobuf-7.34.1-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:d8b2cc79c4d8f62b293ad9b11ec3aebce9af481fa73e64556969f7345ebf9fc7", size = 429247, upload-time = "2026-03-20T17:34:37.024Z" }, + { url = "https://files.pythonhosted.org/packages/eb/9d/aa69df2724ff63efa6f72307b483ce0827f4347cc6d6df24b59e26659fef/protobuf-7.34.1-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:5185e0e948d07abe94bb76ec9b8416b604cfe5da6f871d67aad30cbf24c3110b", size = 325753, upload-time = "2026-03-20T17:34:38.751Z" }, + { url = "https://files.pythonhosted.org/packages/92/e8/d174c91fd48e50101943f042b09af9029064810b734e4160bbe282fa1caa/protobuf-7.34.1-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:403b093a6e28a960372b44e5eb081775c9b056e816a8029c61231743d63f881a", size = 340198, upload-time = "2026-03-20T17:34:39.871Z" }, + { url = "https://files.pythonhosted.org/packages/53/1b/3b431694a4dc6d37b9f653f0c64b0a0d9ec074ee810710c0c3da21d67ba7/protobuf-7.34.1-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:8ff40ce8cd688f7265326b38d5a1bed9bfdf5e6723d49961432f83e21d5713e4", size = 324267, upload-time = "2026-03-20T17:34:41.1Z" }, + { url = "https://files.pythonhosted.org/packages/85/29/64de04a0ac142fb685fd09999bc3d337943fb386f3a0ec57f92fd8203f97/protobuf-7.34.1-cp310-abi3-win32.whl", hash = "sha256:34b84ce27680df7cca9f231043ada0daa55d0c44a2ddfaa58ec1d0d89d8bf60a", size = 426628, upload-time = "2026-03-20T17:34:42.536Z" }, + { url = "https://files.pythonhosted.org/packages/4d/87/cb5e585192a22b8bd457df5a2c16a75ea0db9674c3a0a39fc9347d84e075/protobuf-7.34.1-cp310-abi3-win_amd64.whl", hash = "sha256:e97b55646e6ce5cbb0954a8c28cd39a5869b59090dfaa7df4598a7fba869468c", size = 437901, upload-time = "2026-03-20T17:34:44.112Z" }, + { url = "https://files.pythonhosted.org/packages/88/95/608f665226bca68b736b79e457fded9a2a38c4f4379a4a7614303d9db3bc/protobuf-7.34.1-py3-none-any.whl", hash = "sha256:bb3812cd53aefea2b028ef42bd780f5b96407247f20c6ef7c679807e9d188f11", size = 170715, upload-time = "2026-03-20T17:34:45.384Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.12.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564, upload-time = "2026-03-13T19:27:37.25Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726, upload-time = "2026-03-13T19:27:35.677Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.32.1.20251210" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" }, +] + +[[package]] +name = "types-protobuf" +version = "7.34.1.20260508" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/05/1b/4ae76c732caa3ff5786ac9039d592d526ff2db898da93176c40b63e0b110/types_protobuf-7.34.1.20260508.tar.gz", hash = "sha256:1c93e8c294281b76a5255fc21c747db0004694463ac6ea9866ee06da969fa555", size = 68871, upload-time = "2026-05-08T04:46:17.465Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/61/c94036a4690e18059e55451ba5ce6294ea790651a0f5a429ad961e6b9675/types_protobuf-7.34.1.20260508-py3-none-any.whl", hash = "sha256:a5d647381f8651bd505304ed1148b8a7b342781796e0f80e0284c774c2262a09", size = 85984, upload-time = "2026-05-08T04:46:16.422Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "idna", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", version = "0.4.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "yarl" +version = "1.23.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "idna", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", version = "0.5.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/23/6e/beb1beec874a72f23815c1434518bfc4ed2175065173fb138c3705f658d4/yarl-1.23.0.tar.gz", hash = "sha256:53b1ea6ca88ebd4420379c330aea57e258408dd0df9af0992e5de2078dc9f5d5", size = 194676, upload-time = "2026-03-01T22:07:53.373Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8b/0d/9cc638702f6fc3c7a3685bcc8cf2a9ed7d6206e932a49f5242658047ef51/yarl-1.23.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cff6d44cb13d39db2663a22b22305d10855efa0fa8015ddeacc40bc59b9d8107", size = 123764, upload-time = "2026-03-01T22:04:09.7Z" }, + { url = "https://files.pythonhosted.org/packages/7a/35/5a553687c5793df5429cd1db45909d4f3af7eee90014888c208d086a44f0/yarl-1.23.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e4c53f8347cd4200f0d70a48ad059cabaf24f5adc6ba08622a23423bc7efa10d", size = 86282, upload-time = "2026-03-01T22:04:11.892Z" }, + { url = "https://files.pythonhosted.org/packages/68/2e/c5a2234238f8ce37a8312b52801ee74117f576b1539eec8404a480434acc/yarl-1.23.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2a6940a074fb3c48356ed0158a3ca5699c955ee4185b4d7d619be3c327143e05", size = 86053, upload-time = "2026-03-01T22:04:13.292Z" }, + { url = "https://files.pythonhosted.org/packages/74/3f/bbd8ff36fb038622797ffbaf7db314918bb4d76f1cc8a4f9ca7a55fe5195/yarl-1.23.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ed5f69ce7be7902e5c70ea19eb72d20abf7d725ab5d49777d696e32d4fc1811d", size = 99395, upload-time = "2026-03-01T22:04:15.133Z" }, + { url = "https://files.pythonhosted.org/packages/77/04/9516bc4e269d2a3ec9c6779fcdeac51ce5b3a9b0156f06ac7152e5bba864/yarl-1.23.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:389871e65468400d6283c0308e791a640b5ab5c83bcee02a2f51295f95e09748", size = 92143, upload-time = "2026-03-01T22:04:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/c7/63/88802d1f6b1cb1fc67d67a58cd0cf8a1790de4ce7946e434240f1d60ab4a/yarl-1.23.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:dda608c88cf709b1d406bdfcd84d8d63cff7c9e577a403c6108ce8ce9dcc8764", size = 107643, upload-time = "2026-03-01T22:04:18.519Z" }, + { url = "https://files.pythonhosted.org/packages/8e/db/4f9b838f4d8bdd6f0f385aed8bbf21c71ed11a0b9983305c302cbd557815/yarl-1.23.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8c4fe09e0780c6c3bf2b7d4af02ee2394439d11a523bbcf095cf4747c2932007", size = 108700, upload-time = "2026-03-01T22:04:20.373Z" }, + { url = "https://files.pythonhosted.org/packages/50/12/95a1d33f04a79c402664070d43b8b9f72dc18914e135b345b611b0b1f8cc/yarl-1.23.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:31c9921eb8bd12633b41ad27686bbb0b1a2a9b8452bfdf221e34f311e9942ed4", size = 102769, upload-time = "2026-03-01T22:04:23.055Z" }, + { url = "https://files.pythonhosted.org/packages/86/65/91a0285f51321369fd1a8308aa19207520c5f0587772cfc2e03fc2467e90/yarl-1.23.0-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5f10fd85e4b75967468af655228fbfd212bdf66db1c0d135065ce288982eda26", size = 101114, upload-time = "2026-03-01T22:04:25.031Z" }, + { url = "https://files.pythonhosted.org/packages/58/80/c7c8244fc3e5bc483dc71a09560f43b619fab29301a0f0a8f936e42865c7/yarl-1.23.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dbf507e9ef5688bada447a24d68b4b58dd389ba93b7afc065a2ba892bea54769", size = 98883, upload-time = "2026-03-01T22:04:27.281Z" }, + { url = "https://files.pythonhosted.org/packages/86/e7/71ca9cc9ca79c0b7d491216177d1aed559d632947b8ffb0ee60f7d8b23e3/yarl-1.23.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:85e9beda1f591bc73e77ea1c51965c68e98dafd0fec72cdd745f77d727466716", size = 94172, upload-time = "2026-03-01T22:04:28.554Z" }, + { url = "https://files.pythonhosted.org/packages/6a/3f/6c6c8a0fe29c26fb2db2e8d32195bb84ec1bfb8f1d32e7f73b787fcf349b/yarl-1.23.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:0e1fdaa14ef51366d7757b45bde294e95f6c8c049194e793eedb8387c86d5993", size = 107010, upload-time = "2026-03-01T22:04:30.385Z" }, + { url = "https://files.pythonhosted.org/packages/56/38/12730c05e5ad40a76374d440ed8b0899729a96c250516d91c620a6e38fc2/yarl-1.23.0-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:75e3026ab649bf48f9a10c0134512638725b521340293f202a69b567518d94e0", size = 100285, upload-time = "2026-03-01T22:04:31.752Z" }, + { url = "https://files.pythonhosted.org/packages/34/92/6a7be9239f2347234e027284e7a5f74b1140cc86575e7b469d13fba1ebfe/yarl-1.23.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:80e6d33a3d42a7549b409f199857b4fb54e2103fc44fb87605b6663b7a7ff750", size = 108230, upload-time = "2026-03-01T22:04:33.844Z" }, + { url = "https://files.pythonhosted.org/packages/5e/81/4aebccfa9376bd98b9d8bfad20621a57d3e8cfc5b8631c1fa5f62cdd03f4/yarl-1.23.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:5ec2f42d41ccbd5df0270d7df31618a8ee267bfa50997f5d720ddba86c4a83a6", size = 103008, upload-time = "2026-03-01T22:04:35.856Z" }, + { url = "https://files.pythonhosted.org/packages/38/0f/0b4e3edcec794a86b853b0c6396c0a888d72dfce19b2d88c02ac289fb6c1/yarl-1.23.0-cp310-cp310-win32.whl", hash = "sha256:debe9c4f41c32990771be5c22b56f810659f9ddf3d63f67abfdcaa2c6c9c5c1d", size = 83073, upload-time = "2026-03-01T22:04:38.268Z" }, + { url = "https://files.pythonhosted.org/packages/a0/71/ad95c33da18897e4c636528bbc24a1dd23fe16797de8bc4ec667b8db0ba4/yarl-1.23.0-cp310-cp310-win_amd64.whl", hash = "sha256:ab5f043cb8a2d71c981c09c510da013bc79fd661f5c60139f00dd3c3cc4f2ffb", size = 87328, upload-time = "2026-03-01T22:04:39.558Z" }, + { url = "https://files.pythonhosted.org/packages/e2/14/dfa369523c79bccf9c9c746b0a63eb31f65db9418ac01275f7950962e504/yarl-1.23.0-cp310-cp310-win_arm64.whl", hash = "sha256:263cd4f47159c09b8b685890af949195b51d1aa82ba451c5847ca9bc6413c220", size = 82463, upload-time = "2026-03-01T22:04:41.454Z" }, + { url = "https://files.pythonhosted.org/packages/a2/aa/60da938b8f0997ba3a911263c40d82b6f645a67902a490b46f3355e10fae/yarl-1.23.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:b35d13d549077713e4414f927cdc388d62e543987c572baee613bf82f11a4b99", size = 123641, upload-time = "2026-03-01T22:04:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/24/84/e237607faf4e099dbb8a4f511cfd5efcb5f75918baad200ff7380635631b/yarl-1.23.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cbb0fef01f0c6b38cb0f39b1f78fc90b807e0e3c86a7ff3ce74ad77ce5c7880c", size = 86248, upload-time = "2026-03-01T22:04:44.757Z" }, + { url = "https://files.pythonhosted.org/packages/b2/0d/71ceabc14c146ba8ee3804ca7b3d42b1664c8440439de5214d366fec7d3a/yarl-1.23.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:dc52310451fc7c629e13c4e061cbe2dd01684d91f2f8ee2821b083c58bd72432", size = 85988, upload-time = "2026-03-01T22:04:46.365Z" }, + { url = "https://files.pythonhosted.org/packages/8c/6c/4a90d59c572e46b270ca132aca66954f1175abd691f74c1ef4c6711828e2/yarl-1.23.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2c6b50c7b0464165472b56b42d4c76a7b864597007d9c085e8b63e185cf4a7a", size = 100566, upload-time = "2026-03-01T22:04:47.639Z" }, + { url = "https://files.pythonhosted.org/packages/49/fb/c438fb5108047e629f6282a371e6e91cf3f97ee087c4fb748a1f32ceef55/yarl-1.23.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:aafe5dcfda86c8af00386d7781d4c2181b5011b7be3f2add5e99899ea925df05", size = 92079, upload-time = "2026-03-01T22:04:48.925Z" }, + { url = "https://files.pythonhosted.org/packages/d9/13/d269aa1aed3e4f50a5a103f96327210cc5fa5dd2d50882778f13c7a14606/yarl-1.23.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ee33b875f0b390564c1fb7bc528abf18c8ee6073b201c6ae8524aca778e2d83", size = 108741, upload-time = "2026-03-01T22:04:50.838Z" }, + { url = "https://files.pythonhosted.org/packages/85/fb/115b16f22c37ea4437d323e472945bea97301c8ec6089868fa560abab590/yarl-1.23.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:4c41e021bc6d7affb3364dc1e1e5fa9582b470f283748784bd6ea0558f87f42c", size = 108099, upload-time = "2026-03-01T22:04:52.499Z" }, + { url = "https://files.pythonhosted.org/packages/9a/64/c53487d9f4968045b8afa51aed7ca44f58b2589e772f32745f3744476c82/yarl-1.23.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:99c8a9ed30f4164bc4c14b37a90208836cbf50d4ce2a57c71d0f52c7fb4f7598", size = 102678, upload-time = "2026-03-01T22:04:55.176Z" }, + { url = "https://files.pythonhosted.org/packages/85/59/cd98e556fbb2bf8fab29c1a722f67ad45c5f3447cac798ab85620d1e70af/yarl-1.23.0-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f2af5c81a1f124609d5f33507082fc3f739959d4719b56877ab1ee7e7b3d602b", size = 100803, upload-time = "2026-03-01T22:04:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/b39770b56d4a9f0bb5f77e2f1763cd2d75cc2f6c0131e3b4c360348fcd65/yarl-1.23.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b41389c19b07c760c7e427a3462e8ab83c4bb087d127f0e854c706ce1b9215c", size = 100163, upload-time = "2026-03-01T22:04:58.492Z" }, + { url = "https://files.pythonhosted.org/packages/e7/64/6980f99ab00e1f0ff67cb84766c93d595b067eed07439cfccfc8fb28c1a6/yarl-1.23.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:1dc702e42d0684f42d6519c8d581e49c96cefaaab16691f03566d30658ee8788", size = 93859, upload-time = "2026-03-01T22:05:00.268Z" }, + { url = "https://files.pythonhosted.org/packages/38/69/912e6c5e146793e5d4b5fe39ff5b00f4d22463dfd5a162bec565ac757673/yarl-1.23.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:0e40111274f340d32ebcc0a5668d54d2b552a6cca84c9475859d364b380e3222", size = 108202, upload-time = "2026-03-01T22:05:02.273Z" }, + { url = "https://files.pythonhosted.org/packages/59/97/35ca6767524687ad64e5f5c31ad54bc76d585585a9fcb40f649e7e82ffed/yarl-1.23.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:4764a6a7588561a9aef92f65bda2c4fb58fe7c675c0883862e6df97559de0bfb", size = 99866, upload-time = "2026-03-01T22:05:03.597Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1c/1a3387ee6d73589f6f2a220ae06f2984f6c20b40c734989b0a44f5987308/yarl-1.23.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:03214408cfa590df47728b84c679ae4ef00be2428e11630277be0727eba2d7cc", size = 107852, upload-time = "2026-03-01T22:05:04.986Z" }, + { url = "https://files.pythonhosted.org/packages/a4/b8/35c0750fcd5a3f781058bfd954515dd4b1eab45e218cbb85cf11132215f1/yarl-1.23.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:170e26584b060879e29fac213e4228ef063f39128723807a312e5c7fec28eff2", size = 102919, upload-time = "2026-03-01T22:05:06.397Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1c/9a1979aec4a81896d597bcb2177827f2dbee3f5b7cc48b2d0dadb644b41d/yarl-1.23.0-cp311-cp311-win32.whl", hash = "sha256:51430653db848d258336cfa0244427b17d12db63d42603a55f0d4546f50f25b5", size = 82602, upload-time = "2026-03-01T22:05:08.444Z" }, + { url = "https://files.pythonhosted.org/packages/93/22/b85eca6fa2ad9491af48c973e4c8cf6b103a73dbb271fe3346949449fca0/yarl-1.23.0-cp311-cp311-win_amd64.whl", hash = "sha256:bf49a3ae946a87083ef3a34c8f677ae4243f5b824bfc4c69672e72b3d6719d46", size = 87461, upload-time = "2026-03-01T22:05:10.145Z" }, + { url = "https://files.pythonhosted.org/packages/93/95/07e3553fe6f113e6864a20bdc53a78113cda3b9ced8784ee52a52c9f80d8/yarl-1.23.0-cp311-cp311-win_arm64.whl", hash = "sha256:b39cb32a6582750b6cc77bfb3c49c0f8760dc18dc96ec9fb55fbb0f04e08b928", size = 82336, upload-time = "2026-03-01T22:05:11.554Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/94615bc31022f711add374097ad4144d569e95ff3c38d39215d07ac153a0/yarl-1.23.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:1932b6b8bba8d0160a9d1078aae5838a66039e8832d41d2992daa9a3a08f7860", size = 124737, upload-time = "2026-03-01T22:05:12.897Z" }, + { url = "https://files.pythonhosted.org/packages/e3/6f/c6554045d59d64052698add01226bc867b52fe4a12373415d7991fdca95d/yarl-1.23.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:411225bae281f114067578891bc75534cfb3d92a3b4dfef7a6ca78ba354e6069", size = 87029, upload-time = "2026-03-01T22:05:14.376Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/725ecc166d53438bc88f76822ed4b1e3b10756e790bafd7b523fe97c322d/yarl-1.23.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:13a563739ae600a631c36ce096615fe307f131344588b0bc0daec108cdb47b25", size = 86310, upload-time = "2026-03-01T22:05:15.71Z" }, + { url = "https://files.pythonhosted.org/packages/99/30/58260ed98e6ff7f90ba84442c1ddd758c9170d70327394a6227b310cd60f/yarl-1.23.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9cbf44c5cb4a7633d078788e1b56387e3d3cf2b8139a3be38040b22d6c3221c8", size = 97587, upload-time = "2026-03-01T22:05:17.384Z" }, + { url = "https://files.pythonhosted.org/packages/76/0a/8b08aac08b50682e65759f7f8dde98ae8168f72487e7357a5d684c581ef9/yarl-1.23.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:53ad387048f6f09a8969631e4de3f1bf70c50e93545d64af4f751b2498755072", size = 92528, upload-time = "2026-03-01T22:05:18.804Z" }, + { url = "https://files.pythonhosted.org/packages/52/07/0b7179101fe5f8385ec6c6bb5d0cb9f76bd9fb4a769591ab6fb5cdbfc69a/yarl-1.23.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4a59ba56f340334766f3a4442e0efd0af895fae9e2b204741ef885c446b3a1a8", size = 105339, upload-time = "2026-03-01T22:05:20.235Z" }, + { url = "https://files.pythonhosted.org/packages/d3/8a/36d82869ab5ec829ca8574dfcb92b51286fcfb1e9c7a73659616362dc880/yarl-1.23.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:803a3c3ce4acc62eaf01eaca1208dcf0783025ef27572c3336502b9c232005e7", size = 105061, upload-time = "2026-03-01T22:05:22.268Z" }, + { url = "https://files.pythonhosted.org/packages/66/3e/868e5c3364b6cee19ff3e1a122194fa4ce51def02c61023970442162859e/yarl-1.23.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3d2bff8f37f8d0f96c7ec554d16945050d54462d6e95414babaa18bfafc7f51", size = 100132, upload-time = "2026-03-01T22:05:23.638Z" }, + { url = "https://files.pythonhosted.org/packages/cf/26/9c89acf82f08a52cb52d6d39454f8d18af15f9d386a23795389d1d423823/yarl-1.23.0-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c75eb09e8d55bceb4367e83496ff8ef2bc7ea6960efb38e978e8073ea59ecb67", size = 99289, upload-time = "2026-03-01T22:05:25.749Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/5b0db00d2cb056922356104468019c0a132e89c8d3ab67d8ede9f4483d2a/yarl-1.23.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:877b0738624280e34c55680d6054a307aa94f7d52fa0e3034a9cc6e790871da7", size = 96950, upload-time = "2026-03-01T22:05:27.318Z" }, + { url = "https://files.pythonhosted.org/packages/f6/40/10fa93811fd439341fad7e0718a86aca0de9548023bbb403668d6555acab/yarl-1.23.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:b5405bb8f0e783a988172993cfc627e4d9d00432d6bbac65a923041edacf997d", size = 93960, upload-time = "2026-03-01T22:05:28.738Z" }, + { url = "https://files.pythonhosted.org/packages/bc/d2/8ae2e6cd77d0805f4526e30ec43b6f9a3dfc542d401ac4990d178e4bf0cf/yarl-1.23.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c3a3598a832590c5a3ce56ab5576361b5688c12cb1d39429cf5dba30b510760", size = 104703, upload-time = "2026-03-01T22:05:30.438Z" }, + { url = "https://files.pythonhosted.org/packages/2f/0c/b3ceacf82c3fe21183ce35fa2acf5320af003d52bc1fcf5915077681142e/yarl-1.23.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:8419ebd326430d1cbb7efb5292330a2cf39114e82df5cc3d83c9a0d5ebeaf2f2", size = 98325, upload-time = "2026-03-01T22:05:31.835Z" }, + { url = "https://files.pythonhosted.org/packages/9d/e0/12900edd28bdab91a69bd2554b85ad7b151f64e8b521fe16f9ad2f56477a/yarl-1.23.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:be61f6fff406ca40e3b1d84716fde398fc08bc63dd96d15f3a14230a0973ed86", size = 105067, upload-time = "2026-03-01T22:05:33.358Z" }, + { url = "https://files.pythonhosted.org/packages/15/61/74bb1182cf79c9bbe4eb6b1f14a57a22d7a0be5e9cedf8e2d5c2086474c3/yarl-1.23.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3ceb13c5c858d01321b5d9bb65e4cf37a92169ea470b70fec6f236b2c9dd7e34", size = 100285, upload-time = "2026-03-01T22:05:35.4Z" }, + { url = "https://files.pythonhosted.org/packages/69/7f/cd5ef733f2550de6241bd8bd8c3febc78158b9d75f197d9c7baa113436af/yarl-1.23.0-cp312-cp312-win32.whl", hash = "sha256:fffc45637bcd6538de8b85f51e3df3223e4ad89bccbfca0481c08c7fc8b7ed7d", size = 82359, upload-time = "2026-03-01T22:05:36.811Z" }, + { url = "https://files.pythonhosted.org/packages/f5/be/25216a49daeeb7af2bec0db22d5e7df08ed1d7c9f65d78b14f3b74fd72fc/yarl-1.23.0-cp312-cp312-win_amd64.whl", hash = "sha256:f69f57305656a4852f2a7203efc661d8c042e6cc67f7acd97d8667fb448a426e", size = 87674, upload-time = "2026-03-01T22:05:38.171Z" }, + { url = "https://files.pythonhosted.org/packages/d2/35/aeab955d6c425b227d5b7247eafb24f2653fedc32f95373a001af5dfeb9e/yarl-1.23.0-cp312-cp312-win_arm64.whl", hash = "sha256:6e87a6e8735b44816e7db0b2fbc9686932df473c826b0d9743148432e10bb9b9", size = 81879, upload-time = "2026-03-01T22:05:40.006Z" }, + { url = "https://files.pythonhosted.org/packages/9a/4b/a0a6e5d0ee8a2f3a373ddef8a4097d74ac901ac363eea1440464ccbe0898/yarl-1.23.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:16c6994ac35c3e74fb0ae93323bf8b9c2a9088d55946109489667c510a7d010e", size = 123796, upload-time = "2026-03-01T22:05:41.412Z" }, + { url = "https://files.pythonhosted.org/packages/67/b6/8925d68af039b835ae876db5838e82e76ec87b9782ecc97e192b809c4831/yarl-1.23.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4a42e651629dafb64fd5b0286a3580613702b5809ad3f24934ea87595804f2c5", size = 86547, upload-time = "2026-03-01T22:05:42.841Z" }, + { url = "https://files.pythonhosted.org/packages/ae/50/06d511cc4b8e0360d3c94af051a768e84b755c5eb031b12adaaab6dec6e5/yarl-1.23.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c6b9461a2a8b47c65eef63bb1c76a4f1c119618ffa99ea79bc5bb1e46c5821b", size = 85854, upload-time = "2026-03-01T22:05:44.85Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f4/4e30b250927ffdab4db70da08b9b8d2194d7c7b400167b8fbeca1e4701ca/yarl-1.23.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2569b67d616eab450d262ca7cb9f9e19d2f718c70a8b88712859359d0ab17035", size = 98351, upload-time = "2026-03-01T22:05:46.836Z" }, + { url = "https://files.pythonhosted.org/packages/86/fc/4118c5671ea948208bdb1492d8b76bdf1453d3e73df051f939f563e7dcc5/yarl-1.23.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e9d9a4d06d3481eab79803beb4d9bd6f6a8e781ec078ac70d7ef2dcc29d1bea5", size = 92711, upload-time = "2026-03-01T22:05:48.316Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/1ed91d42bd9e73c13dc9e7eb0dd92298d75e7ac4dd7f046ad0c472e231cd/yarl-1.23.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f514f6474e04179d3d33175ed3f3e31434d3130d42ec153540d5b157deefd735", size = 106014, upload-time = "2026-03-01T22:05:50.028Z" }, + { url = "https://files.pythonhosted.org/packages/ce/c9/74e44e056a23fbc33aca71779ef450ca648a5bc472bdad7a82339918f818/yarl-1.23.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fda207c815b253e34f7e1909840fd14299567b1c0eb4908f8c2ce01a41265401", size = 105557, upload-time = "2026-03-01T22:05:51.416Z" }, + { url = "https://files.pythonhosted.org/packages/66/fe/b1e10b08d287f518994f1e2ff9b6d26f0adeecd8dd7d533b01bab29a3eda/yarl-1.23.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:34b6cf500e61c90f305094911f9acc9c86da1a05a7a3f5be9f68817043f486e4", size = 101559, upload-time = "2026-03-01T22:05:52.872Z" }, + { url = "https://files.pythonhosted.org/packages/72/59/c5b8d94b14e3d3c2a9c20cb100119fd534ab5a14b93673ab4cc4a4141ea5/yarl-1.23.0-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d7504f2b476d21653e4d143f44a175f7f751cd41233525312696c76aa3dbb23f", size = 100502, upload-time = "2026-03-01T22:05:54.954Z" }, + { url = "https://files.pythonhosted.org/packages/77/4f/96976cb54cbfc5c9fd73ed4c51804f92f209481d1fb190981c0f8a07a1d7/yarl-1.23.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:578110dd426f0d209d1509244e6d4a3f1a3e9077655d98c5f22583d63252a08a", size = 98027, upload-time = "2026-03-01T22:05:56.409Z" }, + { url = "https://files.pythonhosted.org/packages/63/6e/904c4f476471afdbad6b7e5b70362fb5810e35cd7466529a97322b6f5556/yarl-1.23.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:609d3614d78d74ebe35f54953c5bbd2ac647a7ddb9c30a5d877580f5e86b22f2", size = 95369, upload-time = "2026-03-01T22:05:58.141Z" }, + { url = "https://files.pythonhosted.org/packages/9d/40/acfcdb3b5f9d68ef499e39e04d25e141fe90661f9d54114556cf83be8353/yarl-1.23.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:4966242ec68afc74c122f8459abd597afd7d8a60dc93d695c1334c5fd25f762f", size = 105565, upload-time = "2026-03-01T22:06:00.286Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/31e28f3a6ba2869c43d124f37ea5260cac9c9281df803c354b31f4dd1f3c/yarl-1.23.0-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:e0fd068364a6759bc794459f0a735ab151d11304346332489c7972bacbe9e72b", size = 99813, upload-time = "2026-03-01T22:06:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/08/1f/6f65f59e72d54aa467119b63fc0b0b1762eff0232db1f4720cd89e2f4a17/yarl-1.23.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:39004f0ad156da43e86aa71f44e033de68a44e5a31fc53507b36dd253970054a", size = 105632, upload-time = "2026-03-01T22:06:03.188Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c4/18b178a69935f9e7a338127d5b77d868fdc0f0e49becd286d51b3a18c61d/yarl-1.23.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e5723c01a56c5028c807c701aa66722916d2747ad737a046853f6c46f4875543", size = 101895, upload-time = "2026-03-01T22:06:04.651Z" }, + { url = "https://files.pythonhosted.org/packages/8f/54/f5b870b5505663911dba950a8e4776a0dbd51c9c54c0ae88e823e4b874a0/yarl-1.23.0-cp313-cp313-win32.whl", hash = "sha256:1b6b572edd95b4fa8df75de10b04bc81acc87c1c7d16bcdd2035b09d30acc957", size = 82356, upload-time = "2026-03-01T22:06:06.04Z" }, + { url = "https://files.pythonhosted.org/packages/7a/84/266e8da36879c6edcd37b02b547e2d9ecdfea776be49598e75696e3316e1/yarl-1.23.0-cp313-cp313-win_amd64.whl", hash = "sha256:baaf55442359053c7d62f6f8413a62adba3205119bcb6f49594894d8be47e5e3", size = 87515, upload-time = "2026-03-01T22:06:08.107Z" }, + { url = "https://files.pythonhosted.org/packages/00/fd/7e1c66efad35e1649114fa13f17485f62881ad58edeeb7f49f8c5e748bf9/yarl-1.23.0-cp313-cp313-win_arm64.whl", hash = "sha256:fb4948814a2a98e3912505f09c9e7493b1506226afb1f881825368d6fb776ee3", size = 81785, upload-time = "2026-03-01T22:06:10.181Z" }, + { url = "https://files.pythonhosted.org/packages/9c/fc/119dd07004f17ea43bb91e3ece6587759edd7519d6b086d16bfbd3319982/yarl-1.23.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:aecfed0b41aa72b7881712c65cf764e39ce2ec352324f5e0837c7048d9e6daaa", size = 130719, upload-time = "2026-03-01T22:06:11.708Z" }, + { url = "https://files.pythonhosted.org/packages/e6/0d/9f2348502fbb3af409e8f47730282cd6bc80dec6630c1e06374d882d6eb2/yarl-1.23.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a41bcf68efd19073376eb8cf948b8d9be0af26256403e512bb18f3966f1f9120", size = 89690, upload-time = "2026-03-01T22:06:13.429Z" }, + { url = "https://files.pythonhosted.org/packages/50/93/e88f3c80971b42cfc83f50a51b9d165a1dbf154b97005f2994a79f212a07/yarl-1.23.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cde9a2ecd91668bcb7f077c4966d8ceddb60af01b52e6e3e2680e4cf00ad1a59", size = 89851, upload-time = "2026-03-01T22:06:15.53Z" }, + { url = "https://files.pythonhosted.org/packages/1c/07/61c9dd8ba8f86473263b4036f70fb594c09e99c0d9737a799dfd8bc85651/yarl-1.23.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5023346c4ee7992febc0068e7593de5fa2bf611848c08404b35ebbb76b1b0512", size = 95874, upload-time = "2026-03-01T22:06:17.553Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e9/f9ff8ceefba599eac6abddcfb0b3bee9b9e636e96dbf54342a8577252379/yarl-1.23.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1009abedb49ae95b136a8904a3f71b342f849ffeced2d3747bf29caeda218c4", size = 88710, upload-time = "2026-03-01T22:06:19.004Z" }, + { url = "https://files.pythonhosted.org/packages/eb/78/0231bfcc5d4c8eec220bc2f9ef82cb4566192ea867a7c5b4148f44f6cbcd/yarl-1.23.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a8d00f29b42f534cc8aa3931cfe773b13b23e561e10d2b26f27a8d309b0e82a1", size = 101033, upload-time = "2026-03-01T22:06:21.203Z" }, + { url = "https://files.pythonhosted.org/packages/cd/9b/30ea5239a61786f18fd25797151a17fbb3be176977187a48d541b5447dd4/yarl-1.23.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:95451e6ce06c3e104556d73b559f5da6c34a069b6b62946d3ad66afcd51642ea", size = 100817, upload-time = "2026-03-01T22:06:22.738Z" }, + { url = "https://files.pythonhosted.org/packages/62/e2/a4980481071791bc83bce2b7a1a1f7adcabfa366007518b4b845e92eeee3/yarl-1.23.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:531ef597132086b6cf96faa7c6c1dcd0361dd5f1694e5cc30375907b9b7d3ea9", size = 97482, upload-time = "2026-03-01T22:06:24.21Z" }, + { url = "https://files.pythonhosted.org/packages/e5/1e/304a00cf5f6100414c4b5a01fc7ff9ee724b62158a08df2f8170dfc72a2d/yarl-1.23.0-cp313-cp313t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:88f9fb0116fbfcefcab70f85cf4b74a2b6ce5d199c41345296f49d974ddb4123", size = 95949, upload-time = "2026-03-01T22:06:25.697Z" }, + { url = "https://files.pythonhosted.org/packages/68/03/093f4055ed4cae649ac53bca3d180bd37102e9e11d048588e9ab0c0108d0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:e7b0460976dc75cb87ad9cc1f9899a4b97751e7d4e77ab840fc9b6d377b8fd24", size = 95839, upload-time = "2026-03-01T22:06:27.309Z" }, + { url = "https://files.pythonhosted.org/packages/b9/28/4c75ebb108f322aa8f917ae10a8ffa4f07cae10a8a627b64e578617df6a0/yarl-1.23.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:115136c4a426f9da976187d238e84139ff6b51a20839aa6e3720cd1026d768de", size = 90696, upload-time = "2026-03-01T22:06:29.048Z" }, + { url = "https://files.pythonhosted.org/packages/23/9c/42c2e2dd91c1a570402f51bdf066bfdb1241c2240ba001967bad778e77b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:ead11956716a940c1abc816b7df3fa2b84d06eaed8832ca32f5c5e058c65506b", size = 100865, upload-time = "2026-03-01T22:06:30.525Z" }, + { url = "https://files.pythonhosted.org/packages/74/05/1bcd60a8a0a914d462c305137246b6f9d167628d73568505fce3f1cb2e65/yarl-1.23.0-cp313-cp313t-musllinux_1_2_riscv64.whl", hash = "sha256:fe8f8f5e70e6dbdfca9882cd9deaac058729bcf323cf7a58660901e55c9c94f6", size = 96234, upload-time = "2026-03-01T22:06:32.692Z" }, + { url = "https://files.pythonhosted.org/packages/90/b2/f52381aac396d6778ce516b7bc149c79e65bfc068b5de2857ab69eeea3b7/yarl-1.23.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:a0e317df055958a0c1e79e5d2aa5a5eaa4a6d05a20d4b0c9c3f48918139c9fc6", size = 100295, upload-time = "2026-03-01T22:06:34.268Z" }, + { url = "https://files.pythonhosted.org/packages/e5/e8/638bae5bbf1113a659b2435d8895474598afe38b4a837103764f603aba56/yarl-1.23.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f0fd84de0c957b2d280143522c4f91a73aada1923caee763e24a2b3fda9f8a5", size = 97784, upload-time = "2026-03-01T22:06:35.864Z" }, + { url = "https://files.pythonhosted.org/packages/80/25/a3892b46182c586c202629fc2159aa13975d3741d52ebd7347fd501d48d5/yarl-1.23.0-cp313-cp313t-win32.whl", hash = "sha256:93a784271881035ab4406a172edb0faecb6e7d00f4b53dc2f55919d6c9688595", size = 88313, upload-time = "2026-03-01T22:06:37.39Z" }, + { url = "https://files.pythonhosted.org/packages/43/68/8c5b36aa5178900b37387937bc2c2fe0e9505537f713495472dcf6f6fccc/yarl-1.23.0-cp313-cp313t-win_amd64.whl", hash = "sha256:dd00607bffbf30250fe108065f07453ec124dbf223420f57f5e749b04295e090", size = 94932, upload-time = "2026-03-01T22:06:39.579Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cc/d79ba8292f51f81f4dc533a8ccfb9fc6992cabf0998ed3245de7589dc07c/yarl-1.23.0-cp313-cp313t-win_arm64.whl", hash = "sha256:ac09d42f48f80c9ee1635b2fcaa819496a44502737660d3c0f2ade7526d29144", size = 84786, upload-time = "2026-03-01T22:06:41.988Z" }, + { url = "https://files.pythonhosted.org/packages/90/98/b85a038d65d1b92c3903ab89444f48d3cee490a883477b716d7a24b1a78c/yarl-1.23.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:21d1b7305a71a15b4794b5ff22e8eef96ff4a6d7f9657155e5aa419444b28912", size = 124455, upload-time = "2026-03-01T22:06:43.615Z" }, + { url = "https://files.pythonhosted.org/packages/39/54/bc2b45559f86543d163b6e294417a107bb87557609007c007ad889afec18/yarl-1.23.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:85610b4f27f69984932a7abbe52703688de3724d9f72bceb1cca667deff27474", size = 86752, upload-time = "2026-03-01T22:06:45.425Z" }, + { url = "https://files.pythonhosted.org/packages/24/f9/e8242b68362bffe6fb536c8db5076861466fc780f0f1b479fc4ffbebb128/yarl-1.23.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23f371bd662cf44a7630d4d113101eafc0cfa7518a2760d20760b26021454719", size = 86291, upload-time = "2026-03-01T22:06:46.974Z" }, + { url = "https://files.pythonhosted.org/packages/ea/d8/d1cb2378c81dd729e98c716582b1ccb08357e8488e4c24714658cc6630e8/yarl-1.23.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c4a80f77dc1acaaa61f0934176fccca7096d9b1ff08c8ba9cddf5ae034a24319", size = 99026, upload-time = "2026-03-01T22:06:48.459Z" }, + { url = "https://files.pythonhosted.org/packages/0a/ff/7196790538f31debe3341283b5b0707e7feb947620fc5e8236ef28d44f72/yarl-1.23.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:bd654fad46d8d9e823afbb4f87c79160b5a374ed1ff5bde24e542e6ba8f41434", size = 92355, upload-time = "2026-03-01T22:06:50.306Z" }, + { url = "https://files.pythonhosted.org/packages/c1/56/25d58c3eddde825890a5fe6aa1866228377354a3c39262235234ab5f616b/yarl-1.23.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:682bae25f0a0dd23a056739f23a134db9f52a63e2afd6bfb37ddc76292bbd723", size = 106417, upload-time = "2026-03-01T22:06:52.1Z" }, + { url = "https://files.pythonhosted.org/packages/51/8a/882c0e7bc8277eb895b31bce0138f51a1ba551fc2e1ec6753ffc1e7c1377/yarl-1.23.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a82836cab5f197a0514235aaf7ffccdc886ccdaa2324bc0aafdd4ae898103039", size = 106422, upload-time = "2026-03-01T22:06:54.424Z" }, + { url = "https://files.pythonhosted.org/packages/42/2b/fef67d616931055bf3d6764885990a3ac647d68734a2d6a9e1d13de437a2/yarl-1.23.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c57676bdedc94cd3bc37724cf6f8cd2779f02f6aba48de45feca073e714fe52", size = 101915, upload-time = "2026-03-01T22:06:55.895Z" }, + { url = "https://files.pythonhosted.org/packages/18/6a/530e16aebce27c5937920f3431c628a29a4b6b430fab3fd1c117b26ff3f6/yarl-1.23.0-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c7f8dc16c498ff06497c015642333219871effba93e4a2e8604a06264aca5c5c", size = 100690, upload-time = "2026-03-01T22:06:58.21Z" }, + { url = "https://files.pythonhosted.org/packages/88/08/93749219179a45e27b036e03260fda05190b911de8e18225c294ac95bbc9/yarl-1.23.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:5ee586fb17ff8f90c91cf73c6108a434b02d69925f44f5f8e0d7f2f260607eae", size = 98750, upload-time = "2026-03-01T22:06:59.794Z" }, + { url = "https://files.pythonhosted.org/packages/d9/cf/ea424a004969f5d81a362110a6ac1496d79efdc6d50c2c4b2e3ea0fc2519/yarl-1.23.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:17235362f580149742739cc3828b80e24029d08cbb9c4bda0242c7b5bc610a8e", size = 94685, upload-time = "2026-03-01T22:07:01.375Z" }, + { url = "https://files.pythonhosted.org/packages/e2/b7/14341481fe568e2b0408bcf1484c652accafe06a0ade9387b5d3fd9df446/yarl-1.23.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0793e2bd0cf14234983bbb371591e6bea9e876ddf6896cdcc93450996b0b5c85", size = 106009, upload-time = "2026-03-01T22:07:03.151Z" }, + { url = "https://files.pythonhosted.org/packages/0a/e6/5c744a9b54f4e8007ad35bce96fbc9218338e84812d36f3390cea616881a/yarl-1.23.0-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:3650dc2480f94f7116c364096bc84b1d602f44224ef7d5c7208425915c0475dd", size = 100033, upload-time = "2026-03-01T22:07:04.701Z" }, + { url = "https://files.pythonhosted.org/packages/0c/23/e3bfc188d0b400f025bc49d99793d02c9abe15752138dcc27e4eaf0c4a9e/yarl-1.23.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f40e782d49630ad384db66d4d8b73ff4f1b8955dc12e26b09a3e3af064b3b9d6", size = 106483, upload-time = "2026-03-01T22:07:06.231Z" }, + { url = "https://files.pythonhosted.org/packages/72/42/f0505f949a90b3f8b7a363d6cbdf398f6e6c58946d85c6d3a3bc70595b26/yarl-1.23.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:94f8575fbdf81749008d980c17796097e645574a3b8c28ee313931068dad14fe", size = 102175, upload-time = "2026-03-01T22:07:08.4Z" }, + { url = "https://files.pythonhosted.org/packages/aa/65/b39290f1d892a9dd671d1c722014ca062a9c35d60885d57e5375db0404b5/yarl-1.23.0-cp314-cp314-win32.whl", hash = "sha256:c8aa34a5c864db1087d911a0b902d60d203ea3607d91f615acd3f3108ac32169", size = 83871, upload-time = "2026-03-01T22:07:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/a9/5b/9b92f54c784c26e2a422e55a8d2607ab15b7ea3349e28359282f84f01d43/yarl-1.23.0-cp314-cp314-win_amd64.whl", hash = "sha256:63e92247f383c85ab00dd0091e8c3fa331a96e865459f5ee80353c70a4a42d70", size = 89093, upload-time = "2026-03-01T22:07:11.501Z" }, + { url = "https://files.pythonhosted.org/packages/e0/7d/8a84dc9381fd4412d5e7ff04926f9865f6372b4c2fd91e10092e65d29eb8/yarl-1.23.0-cp314-cp314-win_arm64.whl", hash = "sha256:70efd20be968c76ece7baa8dafe04c5be06abc57f754d6f36f3741f7aa7a208e", size = 83384, upload-time = "2026-03-01T22:07:13.069Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/d2fad34b1c08aa161b74394183daa7d800141aaaee207317e82c790b418d/yarl-1.23.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:9a18d6f9359e45722c064c97464ec883eb0e0366d33eda61cb19a244bf222679", size = 131019, upload-time = "2026-03-01T22:07:14.903Z" }, + { url = "https://files.pythonhosted.org/packages/19/ff/33009a39d3ccf4b94d7d7880dfe17fb5816c5a4fe0096d9b56abceea9ac7/yarl-1.23.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:2803ed8b21ca47a43da80a6fd1ed3019d30061f7061daa35ac54f63933409412", size = 89894, upload-time = "2026-03-01T22:07:17.372Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f1/dab7ac5e7306fb79c0190766a3c00b4cb8d09a1f390ded68c85a5934faf5/yarl-1.23.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:394906945aa8b19fc14a61cf69743a868bb8c465efe85eee687109cc540b98f4", size = 89979, upload-time = "2026-03-01T22:07:19.361Z" }, + { url = "https://files.pythonhosted.org/packages/aa/b1/08e95f3caee1fad6e65017b9f26c1d79877b502622d60e517de01e72f95d/yarl-1.23.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:71d006bee8397a4a89f469b8deb22469fe7508132d3c17fa6ed871e79832691c", size = 95943, upload-time = "2026-03-01T22:07:21.266Z" }, + { url = "https://files.pythonhosted.org/packages/c0/cc/6409f9018864a6aa186c61175b977131f373f1988e198e031236916e87e4/yarl-1.23.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:62694e275c93d54f7ccedcfef57d42761b2aad5234b6be1f3e3026cae4001cd4", size = 88786, upload-time = "2026-03-01T22:07:23.129Z" }, + { url = "https://files.pythonhosted.org/packages/76/40/cc22d1d7714b717fde2006fad2ced5efe5580606cb059ae42117542122f3/yarl-1.23.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31de1613658308efdb21ada98cbc86a97c181aa050ba22a808120bb5be3ab94", size = 101307, upload-time = "2026-03-01T22:07:24.689Z" }, + { url = "https://files.pythonhosted.org/packages/8f/0d/476c38e85ddb4c6ec6b20b815bdd779aa386a013f3d8b85516feee55c8dc/yarl-1.23.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:fb1e8b8d66c278b21d13b0a7ca22c41dd757a7c209c6b12c313e445c31dd3b28", size = 100904, upload-time = "2026-03-01T22:07:26.287Z" }, + { url = "https://files.pythonhosted.org/packages/72/32/0abe4a76d59adf2081dcb0397168553ece4616ada1c54d1c49d8936c74f8/yarl-1.23.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50f9d8d531dfb767c565f348f33dd5139a6c43f5cbdf3f67da40d54241df93f6", size = 97728, upload-time = "2026-03-01T22:07:27.906Z" }, + { url = "https://files.pythonhosted.org/packages/b7/35/7b30f4810fba112f60f5a43237545867504e15b1c7647a785fbaf588fac2/yarl-1.23.0-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:575aa4405a656e61a540f4a80eaa5260f2a38fff7bfdc4b5f611840d76e9e277", size = 95964, upload-time = "2026-03-01T22:07:30.198Z" }, + { url = "https://files.pythonhosted.org/packages/2d/86/ed7a73ab85ef00e8bb70b0cb5421d8a2a625b81a333941a469a6f4022828/yarl-1.23.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:041b1a4cefacf65840b4e295c6985f334ba83c30607441ae3cf206a0eed1a2e4", size = 95882, upload-time = "2026-03-01T22:07:32.132Z" }, + { url = "https://files.pythonhosted.org/packages/19/90/d56967f61a29d8498efb7afb651e0b2b422a1e9b47b0ab5f4e40a19b699b/yarl-1.23.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:d38c1e8231722c4ce40d7593f28d92b5fc72f3e9774fe73d7e800ec32299f63a", size = 90797, upload-time = "2026-03-01T22:07:34.404Z" }, + { url = "https://files.pythonhosted.org/packages/72/00/8b8f76909259f56647adb1011d7ed8b321bcf97e464515c65016a47ecdf0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d53834e23c015ee83a99377db6e5e37d8484f333edb03bd15b4bc312cc7254fb", size = 101023, upload-time = "2026-03-01T22:07:35.953Z" }, + { url = "https://files.pythonhosted.org/packages/ac/e2/cab11b126fb7d440281b7df8e9ddbe4851e70a4dde47a202b6642586b8d9/yarl-1.23.0-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:2e27c8841126e017dd2a054a95771569e6070b9ee1b133366d8b31beb5018a41", size = 96227, upload-time = "2026-03-01T22:07:37.594Z" }, + { url = "https://files.pythonhosted.org/packages/c2/9b/2c893e16bfc50e6b2edf76c1a9eb6cb0c744346197e74c65e99ad8d634d0/yarl-1.23.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:76855800ac56f878847a09ce6dba727c93ca2d89c9e9d63002d26b916810b0a2", size = 100302, upload-time = "2026-03-01T22:07:39.334Z" }, + { url = "https://files.pythonhosted.org/packages/28/ec/5498c4e3a6d5f1003beb23405671c2eb9cdbf3067d1c80f15eeafe301010/yarl-1.23.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:e09fd068c2e169a7070d83d3bde728a4d48de0549f975290be3c108c02e499b4", size = 98202, upload-time = "2026-03-01T22:07:41.717Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c3/cd737e2d45e70717907f83e146f6949f20cc23cd4bf7b2688727763aa458/yarl-1.23.0-cp314-cp314t-win32.whl", hash = "sha256:73309162a6a571d4cbd3b6a1dcc703c7311843ae0d1578df6f09be4e98df38d4", size = 90558, upload-time = "2026-03-01T22:07:43.433Z" }, + { url = "https://files.pythonhosted.org/packages/e1/19/3774d162f6732d1cfb0b47b4140a942a35ca82bb19b6db1f80e9e7bdc8f8/yarl-1.23.0-cp314-cp314t-win_amd64.whl", hash = "sha256:4503053d296bc6e4cbd1fad61cf3b6e33b939886c4f249ba7c78b602214fabe2", size = 97610, upload-time = "2026-03-01T22:07:45.773Z" }, + { url = "https://files.pythonhosted.org/packages/51/47/3fa2286c3cb162c71cdb34c4224d5745a1ceceb391b2bd9b19b668a8d724/yarl-1.23.0-cp314-cp314t-win_arm64.whl", hash = "sha256:44bb7bef4ea409384e3f8bc36c063d77ea1b8d4a5b2706956c0d6695f07dcc25", size = 86041, upload-time = "2026-03-01T22:07:49.026Z" }, + { url = "https://files.pythonhosted.org/packages/69/68/c8739671f5699c7dc470580a4f821ef37c32c4cb0b047ce223a7f115757f/yarl-1.23.0-py3-none-any.whl", hash = "sha256:a2df6afe50dea8ae15fa34c9f824a3ee958d785fd5d089063d960bae1daa0a3f", size = 48288, upload-time = "2026-03-01T22:07:51.388Z" }, +] diff --git a/examples/multiple_connections.py b/examples/multiple_connections.py new file mode 100644 index 00000000..a3d03771 --- /dev/null +++ b/examples/multiple_connections.py @@ -0,0 +1,56 @@ +import os +import asyncio +from livekit import api, rtc + +# This example demonstrates running multiple connections sequentially in the same thread. +# This is useful when interoperating with a synchronous framework like Django or Flask +# where you would connect to a LiveKit room as part of a request handler. + +# LIVEKIT_URL needs to be set +# also, set either LIVEKIT_TOKEN, or API_KEY and API_SECRET + + +async def main(): + url = os.environ["LIVEKIT_URL"] + token = os.getenv("LIVEKIT_TOKEN") + room = rtc.Room() + if not token: + token = ( + api.AccessToken() + .with_identity("python-bot") + .with_name("Python Bot") + .with_grants( + api.VideoGrants( + room_join=True, + room="my-room", + ) + ) + .to_jwt() + ) + + track_sub = asyncio.Event() + + @room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ): + if track.kind == rtc.TrackKind.KIND_AUDIO: + stream = rtc.AudioStream(track) # the error comes from this line + track_sub.set() + # any created streams would need to be closed explicitly to avoid leaks + asyncio.get_event_loop().create_task(stream.aclose()) + print("subscribed to audio track") + + await room.connect(url, token) + print(f"connected to room: {room.name}") + await track_sub.wait() + await room.disconnect() + print("disconnected from room") + + +if __name__ == "__main__": + asyncio.run(main()) + asyncio.run(main()) + print("successfully ran multiple connections") diff --git a/examples/participant_attributes.py b/examples/participant_attributes.py new file mode 100644 index 00000000..cf203948 --- /dev/null +++ b/examples/participant_attributes.py @@ -0,0 +1,71 @@ +import asyncio +import logging +from signal import SIGINT, SIGTERM +import os + +from livekit import api, rtc + +# ensure LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are set + + +async def main(room: rtc.Room) -> None: + token = ( + api.AccessToken() + .with_identity("python-bot") + .with_name("Python Bot") + .with_grants( + api.VideoGrants( + room_join=True, + room="my-room", + can_update_own_metadata=True, + ) + ) + .to_jwt() + ) + + @room.on("participant_attributes_changed") + def on_participant_attributes_changed( + changed_attributes: dict[str, str], participant: rtc.Participant + ): + logging.info( + "participant attributes changed: %s %s", + participant.attributes, + changed_attributes, + ) + + await room.connect(os.getenv("LIVEKIT_URL"), token) + logging.info("connected to room %s", room.name) + + # Create an attribute + await room.local_participant.set_attributes({"foo": "bar"}) + # Delete an attribute + await room.local_participant.set_attributes({"foo": ""}) + + # Create another attribute + await room.local_participant.set_attributes({"baz": "qux"}) + + # Update an attribute + await room.local_participant.set_attributes({"baz": "biz"}) + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[logging.FileHandler("basic_room.log"), logging.StreamHandler()], + ) + + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + async def cleanup(): + await room.disconnect() + loop.stop() + + asyncio.ensure_future(main(room)) + for signal in [SIGINT, SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/examples/play_audio_stream.py b/examples/play_audio_stream.py new file mode 100644 index 00000000..442491d0 --- /dev/null +++ b/examples/play_audio_stream.py @@ -0,0 +1,158 @@ +import asyncio +import os +import numpy as np +import sounddevice as sd + +from livekit import rtc, api +from livekit.plugins import noise_cancellation + +SAMPLERATE = 48000 +BLOCKSIZE = 480 # 10ms chunks at 48kHz +CHANNELS = 1 + + +class AudioBuffer: + def __init__(self, blocksize=BLOCKSIZE): + self.blocksize = blocksize + self.buffer = np.array([], dtype=np.int16) + + def add_frame(self, frame_data): + self.buffer = np.concatenate([self.buffer, frame_data]) + + def get_chunk(self): + if len(self.buffer) >= self.blocksize: + chunk = self.buffer[: self.blocksize] + self.buffer = self.buffer[self.blocksize :] + return chunk + return None + + def get_padded_chunk(self): + if len(self.buffer) > 0: + chunk = np.zeros(self.blocksize, dtype=np.int16) + available = min(len(self.buffer), self.blocksize) + chunk[:available] = self.buffer[:available] + self.buffer = self.buffer[available:] + return chunk + return np.zeros(self.blocksize, dtype=np.int16) + + +async def audio_player(queue: asyncio.Queue): + """Pull from the queue and stream audio using sounddevice.""" + buffer = AudioBuffer(BLOCKSIZE) + + def callback(outdata, frames, time, status): + if status: + print(f"Audio callback status: {status}") + + # Try to fill buffer from queue + while not queue.empty(): + try: + data = queue.get_nowait() + buffer.add_frame(data) + except asyncio.QueueEmpty: + break + + # Get exactly the right amount of data + chunk = buffer.get_chunk() + if chunk is not None: + outdata[:] = chunk.reshape(-1, 1) + else: + # Not enough data, use what we have padded with zeros + outdata[:] = buffer.get_padded_chunk().reshape(-1, 1) + + stream = sd.OutputStream( + samplerate=SAMPLERATE, + channels=CHANNELS, + blocksize=BLOCKSIZE, + dtype="int16", + callback=callback, + latency="low", + ) + with stream: + while True: + await asyncio.sleep(0.1) # keep the loop alive + + +async def rtc_session(room, queue: asyncio.Queue): + track: rtc.RemoteAudioTrack | None = None + while not track: + for participant in room.remote_participants.values(): + for t in participant.track_publications.values(): + if t.kind == rtc.TrackKind.KIND_AUDIO and t.subscribed: + track = t.track + break + if track: + break + if not track: + print("waiting for audio track") + await asyncio.sleep(2) + + stream = rtc.AudioStream.from_track( + track=track, + sample_rate=SAMPLERATE, + num_channels=1, + noise_cancellation=noise_cancellation.BVC(), # or NC() + ) + + print("playing stream") + try: + # Process audio frames from the stream + async for audio_frame_event in stream: + frame = audio_frame_event.frame + + audio_data = np.frombuffer(frame.data, dtype=np.int16) + + try: + await queue.put(audio_data) + except asyncio.QueueFull: + # Skip this frame if queue is full + print("Warning: Audio queue full, dropping frame") + continue + + finally: + # Clean up the stream when done + await stream.aclose() + + +async def main(): + queue = asyncio.Queue(maxsize=50) + player_task = asyncio.create_task(audio_player(queue)) + + token = ( + api.AccessToken() + .with_identity("python-bot") + .with_name("Python Bot") + .with_grants( + api.VideoGrants( + room_join=True, + room="my-room", + agent=True, + ) + ) + .to_jwt() + ) + url = os.getenv("LIVEKIT_URL") + + room = rtc.Room() + await room.connect( + url, + token, + options=rtc.RoomOptions( + auto_subscribe=True, + ), + ) + print(f"Connected to room: {room.name}") + + try: + await rtc_session(room, queue) + finally: + # Clean up + await room.disconnect() + player_task.cancel() + try: + await player_task + except asyncio.CancelledError: + pass + + +asyncio.run(main()) diff --git a/examples/publish_hue.py b/examples/publish_hue.py index 8c26d083..a20a519a 100644 --- a/examples/publish_hue.py +++ b/examples/publish_hue.py @@ -3,12 +3,13 @@ import logging import os from signal import SIGINT, SIGTERM +from time import perf_counter import numpy as np from livekit import api, rtc WIDTH, HEIGHT = 1280, 720 - +FPS = 30 # ensure LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are set @@ -26,6 +27,13 @@ async def main(room: rtc.Room): ) .to_jwt() ) + + @room.on("participant_disconnected") + def on_participant_disconnected(participant: rtc.Participant): + logging.info( + f"participant {participant.identity} disconnected, reason: {rtc.DisconnectReason.Name(participant.disconnect_reason)}" + ) + url = os.getenv("LIVEKIT_URL") logging.info("connecting to %s", url) try: @@ -38,24 +46,51 @@ async def main(room: rtc.Room): # publish a track source = rtc.VideoSource(WIDTH, HEIGHT) track = rtc.LocalVideoTrack.create_video_track("hue", source) - options = rtc.TrackPublishOptions() - options.source = rtc.TrackSource.SOURCE_CAMERA + options = rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + simulcast=True, + video_encoding=rtc.VideoEncoding( + max_framerate=FPS, + max_bitrate=3_000_000, + ), + ) publication = await room.local_participant.publish_track(track, options) logging.info("published track %s", publication.sid) asyncio.ensure_future(draw_color_cycle(source)) + # uncomment the below to test Track Subscription Permissions + # https://docs.livekit.io/home/client/tracks/publish/#subscription-permissions + # await asyncio.sleep(10) + + # logging.info( + # "setting track subscription permissions to False, no one can subscribe to the track" + # ) + # room.local_participant.set_track_subscription_permissions(allow_all_participants=False) + + # await asyncio.sleep(10) + + # logging.info("allowing user to subscribe to the track") + # room.local_participant.set_track_subscription_permissions( + # allow_all_participants=False, + # participant_permissions=[ + # rtc.ParticipantTrackPermission( + # participant_identity="allowed-user-identity", + # allow_all=True, + # ) + # ], + # ) + async def draw_color_cycle(source: rtc.VideoSource): argb_frame = bytearray(WIDTH * HEIGHT * 4) arr = np.frombuffer(argb_frame, dtype=np.uint8) - framerate = 1 / 30 + framerate = 1 / FPS hue = 0.0 + next_frame_time = perf_counter() while True: - start_time = asyncio.get_event_loop().time() - rgb = colorsys.hsv_to_rgb(hue, 1.0, 1.0) rgb = [(x * 255) for x in rgb] # type: ignore @@ -69,8 +104,10 @@ async def draw_color_cycle(source: rtc.VideoSource): source.capture_frame(frame) hue = (hue + framerate / 3) % 1.0 - code_duration = asyncio.get_event_loop().time() - start_time - await asyncio.sleep(1 / 30 - code_duration) + # code_duration = perf_counter() - start_time + next_frame_time += 1 / FPS + await asyncio.sleep(next_frame_time - perf_counter()) + # await asyncio.sleep(1 / FPS - code_duration) if __name__ == "__main__": diff --git a/examples/room_example.py b/examples/room_example.py new file mode 100644 index 00000000..6efb0361 --- /dev/null +++ b/examples/room_example.py @@ -0,0 +1,67 @@ +import os +import logging +import asyncio +from livekit import rtc + +# Set the following environment variables with your own values +TOKEN = os.environ.get("LIVEKIT_TOKEN") +URL = os.environ.get("LIVEKIT_URL") + + +async def main(): + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger(__name__) + room = rtc.Room() + + @room.on("participant_connected") + def on_participant_connected(participant: rtc.RemoteParticipant): + logger.info("participant connected: %s %s", participant.sid, participant.identity) + + async def receive_frames(stream: rtc.VideoStream): + async for frame in stream: + # received a video frame from the track, process it here + pass + + # track_subscribed is emitted whenever the local participant is subscribed to a new track + @room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ): + logger.info("track subscribed: %s", publication.sid) + if track.kind == rtc.TrackKind.KIND_VIDEO: + video_stream = rtc.VideoStream(track) + asyncio.ensure_future(receive_frames(video_stream)) + + # By default, autosubscribe is enabled. The participant will be subscribed to + # all published tracks in the room + await room.connect(URL, TOKEN) + logger.info("connected to room %s", room.name) + + for identity, participant in room.remote_participants.items(): + print(f"identity: {identity}") + print(f"participant: {participant}") + # Now participant is the RemoteParticipant object, not a tuple + print(f"participant sid: {participant.sid}") + print(f"participant identity: {participant.identity}") + print(f"participant name: {participant.name}") + print(f"participant kind: {participant.kind}") + print(f"participant track publications: {participant.track_publications}") + for tid, publication in participant.track_publications.items(): + print(f"\ttrack id: {tid}") + print(f"\t\ttrack publication: {publication}") + print(f"\t\ttrack kind: {publication.kind}") + print(f"\t\ttrack name: {publication.name}") + print(f"\t\ttrack source: {publication.source}") + + print(f"participant metadata: {participant.metadata}") + + +if __name__ == "__main__": + # exit if token and url are not set + if not TOKEN or not URL: + print("TOKEN and URL are required environment variables") + exit(1) + + asyncio.run(main()) diff --git a/examples/rpc.py b/examples/rpc.py new file mode 100644 index 00000000..08f53250 --- /dev/null +++ b/examples/rpc.py @@ -0,0 +1,311 @@ +from livekit import rtc, api +import os +import json +import asyncio +from dotenv import load_dotenv +from livekit.rtc.rpc import RpcInvocationData + +load_dotenv(dotenv_path=".env.local", override=False) +LIVEKIT_API_KEY = os.getenv("LIVEKIT_API_KEY") +LIVEKIT_API_SECRET = os.getenv("LIVEKIT_API_SECRET") +LIVEKIT_URL = os.getenv("LIVEKIT_URL") +if not LIVEKIT_API_KEY or not LIVEKIT_API_SECRET or not LIVEKIT_URL: + raise ValueError("Missing required environment variables. Please check your .env.local file.") + + +async def main(): + rooms = [] # Keep track of all rooms for cleanup + try: + room_name = f"rpc-test-{os.urandom(4).hex()}" + print(f"Connecting participants to room: {room_name}") + + callers_room, greeters_room, math_genius_room = await asyncio.gather( + connect_participant("caller", room_name), + connect_participant("greeter", room_name), + connect_participant("math-genius", room_name), + ) + rooms = [callers_room, greeters_room, math_genius_room] + + register_receiver_methods(greeters_room, math_genius_room) + + try: + print("\n\nRunning greeting example...") + await asyncio.gather(perform_greeting(callers_room)) + except Exception as error: + print("Error:", error) + + try: + print("\n\nRunning send long info example...") + await perform_send_very_long_info(callers_room) + except Exception as error: + print("Error:", error) + + try: + print("\n\nRunning error handling example...") + await perform_divide(callers_room) + except Exception as error: + print("Error:", error) + + try: + print("\n\nRunning math example...") + await perform_square_root(callers_room) + await asyncio.sleep(2) + await perform_quantum_hypergeometric_series(callers_room) + except Exception as error: + print("Error:", error) + + try: + print("\n\nRunning long calculation with timeout...") + await asyncio.create_task(perform_long_calculation(callers_room)) + except Exception as error: + print("Error:", error) + + try: + print("\n\nRunning long calculation with disconnect...") + # Start the long calculation + long_calc_task = asyncio.create_task(perform_long_calculation(callers_room)) + # Wait a bit then disconnect the math genius + await asyncio.sleep(5) + print("\nDisconnecting math genius early...") + await math_genius_room.disconnect() + # Wait for the calculation to fail + await long_calc_task + except Exception as error: + print("Error:", error) + + print("\n\nParticipants done, disconnecting remaining participants...") + await callers_room.disconnect() + await greeters_room.disconnect() + + print("Participants disconnected. Example completed.") + + except KeyboardInterrupt: + print("\nReceived interrupt signal, cleaning up...") + except Exception as e: + print(f"Unexpected error: {e}") + finally: + # Clean up all rooms + print("Disconnecting all participants...") + await asyncio.gather(*(room.disconnect() for room in rooms), return_exceptions=True) + print("Cleanup complete") + + +def register_receiver_methods(greeters_room: rtc.Room, math_genius_room: rtc.Room): + @greeters_room.local_participant.register_rpc_method("arrival") + async def arrival_method( + data: RpcInvocationData, + ): + print(f'[Greeter] Oh {data.caller_identity} arrived and said "{data.payload}"') + await asyncio.sleep(2) + return "Welcome and have a wonderful day!" + + @greeters_room.local_participant.register_rpc_method("exchanging-long-info") + async def exchanging_long_info_method( + data: RpcInvocationData, + ): + print( + f"[Greeter] {data.caller_identity} has arrived and said that its long info is " + f'{len(data.payload)} chars long and starts with: "{data.payload[:20]}..."' + ) + await asyncio.sleep(2) + return "Y" * 20_000 + + @math_genius_room.local_participant.register_rpc_method("square-root") + async def square_root_method( + data: RpcInvocationData, + ): + json_data = json.loads(data.payload) + number = json_data["number"] + print( + f"[Math Genius] I guess {data.caller_identity} wants the square root of {number}. I've only got {data.response_timeout} seconds to respond but I think I can pull it off." + ) + + print("[Math Genius] *doing math*…") + await asyncio.sleep(2) + + result = number**0.5 + print(f"[Math Genius] Aha! It's {result}") + return json.dumps({"result": result}) + + @math_genius_room.local_participant.register_rpc_method("divide") + async def divide_method( + data: RpcInvocationData, + ): + json_data = json.loads(data.payload) + dividend = json_data["dividend"] + divisor = json_data["divisor"] + print(f"[Math Genius] {data.caller_identity} wants to divide {dividend} by {divisor}.") + + result = dividend / divisor + return json.dumps({"result": result}) + + @math_genius_room.local_participant.register_rpc_method("long-calculation") + async def long_calculation_method( + data: RpcInvocationData, + ): + print(f"[Math Genius] Starting a very long calculation for {data.caller_identity}") + print( + f"[Math Genius] This will take 30 seconds even though you're only giving me {data.response_timeout} seconds" + ) + await asyncio.sleep(30) + return json.dumps({"result": "Calculation complete!"}) + + +async def perform_greeting(room: rtc.Room): + print("[Caller] Letting the greeter know that I've arrived") + try: + response = await room.local_participant.perform_rpc( + destination_identity="greeter", method="arrival", payload="Hello" + ) + print(f'[Caller] That\'s nice, the greeter said: "{response}"') + except Exception as error: + print(f"[Caller] RPC call failed: {error}") + raise + + +async def perform_send_very_long_info(room: rtc.Room): + print("[Caller] Sending the greeter a very long message") + try: + response = await room.local_participant.perform_rpc( + destination_identity="greeter", + method="exchanging-long-info", + payload="X" * 20_000, + ) + print( + f"[Caller] The greeter's long info is {len(response)} chars long and " + f'starts with: "{response[:20]}..."' + ) + except Exception as error: + print(f"[Caller] RPC call failed: {error}") + raise + + +async def perform_square_root(room: rtc.Room): + print("[Caller] What's the square root of 16?") + try: + response = await room.local_participant.perform_rpc( + destination_identity="math-genius", + method="square-root", + payload=json.dumps({"number": 16}), + ) + parsed_response = json.loads(response) + print(f"[Caller] Nice, the answer was {parsed_response['result']}") + except Exception as error: + print(f"[Caller] RPC call failed: {error}") + raise + + +async def perform_quantum_hypergeometric_series(room: rtc.Room): + print("[Caller] What's the quantum hypergeometric series of 42?") + try: + response = await room.local_participant.perform_rpc( + destination_identity="math-genius", + method="quantum-hypergeometric-series", + payload=json.dumps({"number": 42}), + ) + parsed_response = json.loads(response) + print(f"[Caller] genius says {parsed_response['result']}!") + except rtc.RpcError as error: + if error.code == rtc.RpcError.ErrorCode.UNSUPPORTED_METHOD: + print("[Caller] Aww looks like the genius doesn't know that one.") + return + print("[Caller] Unexpected error:", error) + raise + except Exception as error: + print("[Caller] Unexpected error:", error) + raise + + +async def perform_divide(room: rtc.Room): + print("[Caller] Let's divide 10 by 0.") + try: + response = await room.local_participant.perform_rpc( + destination_identity="math-genius", + method="divide", + payload=json.dumps({"dividend": 10, "divisor": 0}), + ) + parsed_response = json.loads(response) + print(f"[Caller] The result is {parsed_response['result']}") + except rtc.RpcError as error: + if error.code == rtc.RpcError.ErrorCode.APPLICATION_ERROR: + print("[Caller] Aww something went wrong with that one, lets try something else.") + else: + print(f"[Caller] RPC call failed with unexpected RpcError: {error}") + except Exception as error: + print(f"[Caller] RPC call failed with unexpected error: {error}") + + +async def perform_long_calculation(room: rtc.Room): + print("[Caller] Giving the math genius 10s to complete a long calculation") + try: + response = await room.local_participant.perform_rpc( + destination_identity="math-genius", + method="long-calculation", + payload=json.dumps({}), + response_timeout=10, + ) + parsed_response = json.loads(response) + print(f"[Caller] Result: {parsed_response['result']}") + except rtc.RpcError as error: + if error.code == rtc.RpcError.ErrorCode.RESPONSE_TIMEOUT: + print("[Caller] Math genius took too long to respond") + elif error.code == rtc.RpcError.ErrorCode.RECIPIENT_DISCONNECTED: + print("[Caller] Math genius disconnected before response was received") + else: + print(f"[Caller] Unexpected RPC error: {error}") + except Exception as error: + print(f"[Caller] Unexpected error: {error}") + + +def create_token(identity: str, room_name: str): + token = ( + api.AccessToken(LIVEKIT_API_KEY, LIVEKIT_API_SECRET) + .with_identity(identity) + .with_grants( + api.VideoGrants( + room=room_name, + room_join=True, + can_publish=True, + can_subscribe=True, + ) + ) + ) + return token.to_jwt() + + +async def connect_participant(identity: str, room_name: str) -> rtc.Room: + room = rtc.Room() + token = create_token(identity, room_name) + + def on_disconnected(reason: str): + print(f"[{identity}] Disconnected from room: {reason}") + + room.on("disconnected", on_disconnected) + + await room.connect(LIVEKIT_URL, token) + + async def wait_for_participants(): + if room.remote_participants: + return + participant_connected = asyncio.Event() + + def _on_participant_connected(participant: rtc.RemoteParticipant): + room.off("participant_connected", _on_participant_connected) + participant_connected.set() + + room.on("participant_connected", _on_participant_connected) + await participant_connected.wait() + + try: + await asyncio.wait_for(wait_for_participants(), timeout=5.0) + except asyncio.TimeoutError: + raise TimeoutError("Timed out waiting for participants") + + return room + + +if __name__ == "__main__": + try: + asyncio.run(main()) + except KeyboardInterrupt: + print("\nProgram terminated by user") diff --git a/examples/video-stream/README.md b/examples/video-stream/README.md new file mode 100644 index 00000000..8b02faa2 --- /dev/null +++ b/examples/video-stream/README.md @@ -0,0 +1,48 @@ +# Video and Audio Synchronization Examples + +This example demonstrates how to synchronize video and audio streams using the `AVSynchronizer` utility. + +## AVSynchronizer Usage + +The `AVSynchronizer` helps maintain synchronization between video and audio frames. The key principle is to push the initial synchronized video and audio frames together. After that, subsequent frames will be automatically synchronized according to the configured video FPS and audio sample rate. + +```python +av_sync = AVSynchronizer( + audio_source=audio_source, + video_source=video_source, + video_fps=30.0, + video_queue_size_ms=100 +) + +# Push frames to synchronizer +await av_sync.push(video_frame) +await av_sync.push(audio_frame) +``` + +## Examples + +### 1. Video File Playback (`video_play.py`) + +Demonstrates synchronizing video and audio from separate sources while maintaining sync: + +- Reads video and audio streams separately from a media file (using `av` library) +- Uses separate tasks to push video and audio frames to the synchronizer +- Since the streams are continuous, a larger `queue_size_ms` can be used, though this will increase memory usage + +#### Usage: + +```bash +python video_play.py +``` + +### 2. Audio Visualization (`audio_wave.py`) + +Demonstrates generating video based on audio input: + +- Generates audio frames with alternating sine waves and silence +- Creates video frames visualizing the audio waveform (using `cv2` library) +- Shows how to handle cases with and without audio: + - When audio is present: Push synchronized video and audio frames + - During silence: Push only video frames +- Since video and audio frames are pushed in the same loop, audio frames must be smaller than the audio source queue size to avoid blocking +- Uses a small `queue_size_ms` (e.g. 50ms) to control frame generation speed during silence periods diff --git a/examples/video-stream/audio_wave.py b/examples/video-stream/audio_wave.py new file mode 100644 index 00000000..8c600b9a --- /dev/null +++ b/examples/video-stream/audio_wave.py @@ -0,0 +1,321 @@ +import asyncio +import logging +import os +import signal +import time +from collections import deque +from dataclasses import dataclass +from typing import AsyncIterable, Optional, Union + +import numpy as np +from livekit import rtc, api +import sys + +try: + import cv2 +except ImportError: + raise RuntimeError( + "cv2 is required to run this example, install with `pip install opencv-python`" + ) + +# ensure LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are set + +logger = logging.getLogger(__name__) + + +@dataclass +class MediaInfo: + video_width: int + video_height: int + video_fps: float + audio_sample_rate: int + audio_channels: int + + +class _AudioEndSentinel: + pass + + +async def audio_generator( + media_info: MediaInfo, + output_audio: asyncio.Queue[Union[rtc.AudioFrame, _AudioEndSentinel]], +): + """Generates audio frames with alternating sine wave and silence periods""" + frequency = 480 # Hz + amplitude = 0.5 + period = 7.0 + sine_duration = 5.0 # Duration of sine wave in each period + chunk_size = 1024 + + while True: + current_time = 0.0 + + # Generate audio for sine_duration seconds + while current_time < sine_duration: + t = np.linspace( + current_time, + current_time + chunk_size / media_info.audio_sample_rate, + num=chunk_size, + endpoint=False, + ) + # Create volume envelope using sine wave + volume = np.abs(np.sin(2 * np.pi * current_time / sine_duration)) + samples = amplitude * volume * np.sin(2 * np.pi * frequency * t) + + # Convert to int16, (samples, channels) + samples = (samples[:, np.newaxis] * 32767).astype(np.int16) + if media_info.audio_channels > 1: + samples = np.repeat(samples, media_info.audio_channels, axis=1) + + # Create audio frame + audio_frame = rtc.AudioFrame( + data=samples.tobytes(), + sample_rate=media_info.audio_sample_rate, + num_channels=samples.shape[1], + samples_per_channel=samples.shape[0], + ) + await output_audio.put(audio_frame) + current_time += chunk_size / media_info.audio_sample_rate + await asyncio.sleep(0) + await output_audio.put(_AudioEndSentinel()) + + # Simulate silence + silence_duration = period - sine_duration + await asyncio.sleep(silence_duration) + + +class WaveformVisualizer: + def __init__(self, history_length: int = 1000): + self.history_length = history_length + self.volume_history: deque[float] = deque(maxlen=history_length) + self.start_time = time.time() + + def draw_timestamp(self, canvas: np.ndarray, fps: float): + height, width = canvas.shape[:2] + text = f"{time.time() - self.start_time:.1f}s @ {fps:.1f}fps" + font_face = cv2.FONT_HERSHEY_SIMPLEX + font_scale = 2.0 + thickness = 2 + + (text_width, text_height), baseline = cv2.getTextSize( + text, font_face, font_scale, thickness + ) + x = (width - text_width) // 2 + y = int((height - text_height) * 0.4 + baseline) + cv2.putText(canvas, text, (x, y), font_face, font_scale, (0, 0, 0), thickness) + + def draw_current_wave(self, canvas: np.ndarray, audio_samples: np.ndarray) -> np.ndarray: + """Draw the current waveform and return the current values""" + height, width = canvas.shape[:2] + center_y = height // 2 + 100 + + normalized_samples = audio_samples.astype(np.float32) / 32767.0 + normalized_samples = normalized_samples.mean(axis=1) # (samples,) + num_points = min(width, len(normalized_samples)) + + if len(normalized_samples) > num_points: + indices = np.linspace(0, len(normalized_samples) - 1, num_points, dtype=int) + plot_data = normalized_samples[indices] + else: + plot_data = normalized_samples + + x_coords = np.linspace(0, width, num_points, dtype=int) + y_coords = (plot_data * 200) + center_y + + cv2.line(canvas, (0, center_y), (width, center_y), (200, 200, 200), 1) + points = np.column_stack((x_coords, y_coords.astype(int))) + for i in range(len(points) - 1): + cv2.line(canvas, tuple(points[i]), tuple(points[i + 1]), (0, 255, 0), 2) + + return plot_data + + def draw_volume_history(self, canvas: np.ndarray, current_volume: float): + height, width = canvas.shape[:2] + center_y = height // 2 + + self.volume_history.append(current_volume) + cv2.line(canvas, (0, center_y - 250), (width, center_y - 250), (200, 200, 200), 1) + + volume_x = np.linspace(0, width, len(self.volume_history), dtype=int) + volume_y = center_y - 250 + (np.array(self.volume_history) * 200) + points = np.column_stack((volume_x, volume_y.astype(int))) + for i in range(len(points) - 1): + cv2.line(canvas, tuple(points[i]), tuple(points[i + 1]), (255, 0, 0), 2) + + def draw(self, canvas: np.ndarray, audio_samples: np.ndarray, fps: float): + self.draw_timestamp(canvas, fps) + plot_data = self.draw_current_wave(canvas, audio_samples) + current_volume = np.abs(plot_data).mean() + self.draw_volume_history(canvas, current_volume) + + +async def video_generator( + media_info: MediaInfo, + input_audio: asyncio.Queue[Union[rtc.AudioFrame, _AudioEndSentinel]], + av_sync: rtc.AVSynchronizer, # only used for drawing the actual fps on the video +) -> AsyncIterable[tuple[rtc.VideoFrame, Optional[rtc.AudioFrame]]]: + canvas = np.zeros((media_info.video_height, media_info.video_width, 4), dtype=np.uint8) + canvas.fill(255) + + def _np_to_video_frame(image: np.ndarray) -> rtc.VideoFrame: + return rtc.VideoFrame( + width=image.shape[1], + height=image.shape[0], + type=rtc.VideoBufferType.RGBA, + data=image.tobytes(), + ) + + audio_samples_per_frame = int(media_info.audio_sample_rate / media_info.video_fps) + audio_buffer = np.zeros((0, media_info.audio_channels), dtype=np.int16) + wave_visualizer = WaveformVisualizer() + while True: + try: + # timeout has to be shorter than the frame interval to avoid starvation + audio_frame = await asyncio.wait_for( + input_audio.get(), timeout=0.5 / media_info.video_fps + ) + except asyncio.TimeoutError: + # generate frame without audio (e.g. silence state) + new_frame = canvas.copy() + wave_visualizer.draw(new_frame, np.zeros((1, 2)), av_sync.actual_fps) + video_frame = _np_to_video_frame(new_frame) + yield video_frame, None + + # speed is controlled by the video fps in av_sync + await asyncio.sleep(0) + continue + + if isinstance(audio_frame, _AudioEndSentinel): + # drop the audio buffer when the audio finished + audio_buffer = np.zeros((0, media_info.audio_channels), dtype=np.int16) + continue + + audio_samples = np.frombuffer(audio_frame.data, dtype=np.int16).reshape( + -1, audio_frame.num_channels + ) # (samples, channels) + # accumulate audio samples to the buffer + audio_buffer = np.concatenate([audio_buffer, audio_samples], axis=0) + + while audio_buffer.shape[0] >= audio_samples_per_frame: + sub_samples = audio_buffer[:audio_samples_per_frame, :] + audio_buffer = audio_buffer[audio_samples_per_frame:, :] + + new_frame = canvas.copy() + wave_visualizer.draw(new_frame, sub_samples, av_sync.actual_fps) + video_frame = _np_to_video_frame(new_frame) + sub_audio_frame = rtc.AudioFrame( + data=sub_samples.tobytes(), + sample_rate=audio_frame.sample_rate, + num_channels=sub_samples.shape[1], + samples_per_channel=sub_samples.shape[0], + ) + yield video_frame, sub_audio_frame + + +async def main(room: rtc.Room, room_name: str): + token = ( + api.AccessToken() + .with_identity("python-publisher") + .with_name("Python Publisher") + .with_grants( + api.VideoGrants( + room_join=True, + room=room_name, + agent=True, + ) + ) + .to_jwt() + ) + url = os.getenv("LIVEKIT_URL") + logging.info("connecting to %s", url) + + try: + await room.connect(url, token) + logging.info("connected to room %s", room.name) + except rtc.ConnectError as e: + logging.error("failed to connect to the room: %s", e) + return + + # Create media info + media_info = MediaInfo( + video_width=1280, + video_height=720, + video_fps=30.0, + audio_sample_rate=48000, + audio_channels=2, + ) + + # Create video and audio sources/tracks + queue_size_ms = 50 + video_source = rtc.VideoSource( + width=media_info.video_width, + height=media_info.video_height, + ) + audio_source = rtc.AudioSource( + sample_rate=media_info.audio_sample_rate, + num_channels=media_info.audio_channels, + queue_size_ms=queue_size_ms, + ) + + video_track = rtc.LocalVideoTrack.create_video_track("video", video_source) + audio_track = rtc.LocalAudioTrack.create_audio_track("audio", audio_source) + + # Publish tracks + video_options = rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_CAMERA) + audio_options = rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_MICROPHONE) + + await room.local_participant.publish_track(video_track, video_options) + await room.local_participant.publish_track(audio_track, audio_options) + + # Create AV synchronizer + av_sync = rtc.AVSynchronizer( + audio_source=audio_source, + video_source=video_source, + video_fps=media_info.video_fps, + video_queue_size_ms=queue_size_ms, + ) + + # Start audio generator + audio_queue = asyncio.Queue[Union[rtc.AudioFrame, _AudioEndSentinel]](maxsize=1) + audio_task = asyncio.create_task(audio_generator(media_info, audio_queue)) + + try: + async for video_frame, audio_frame in video_generator( + media_info, audio_queue, av_sync=av_sync + ): + await av_sync.push(video_frame) + if audio_frame: + await av_sync.push(audio_frame) + finally: + audio_task.cancel() + await av_sync.aclose() + await audio_source.aclose() + await video_source.aclose() + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[logging.FileHandler("audio_wave.log"), logging.StreamHandler()], + ) + + if len(sys.argv) != 2: + print("Usage: python audio_wave.py ") + sys.exit(1) + + room_name = sys.argv[1] + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + async def cleanup(): + await room.disconnect() + loop.stop() + + asyncio.ensure_future(main(room, room_name)) + for signal in [signal.SIGINT, signal.SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/examples/video-stream/video_play.py b/examples/video-stream/video_play.py new file mode 100644 index 00000000..f40ed4ec --- /dev/null +++ b/examples/video-stream/video_play.py @@ -0,0 +1,249 @@ +import asyncio +import logging +from dataclasses import dataclass +from pathlib import Path +from typing import AsyncIterable, Union +import sys + +import numpy as np +import os +import signal +from livekit import api +from livekit import rtc + +try: + import av +except ImportError: + raise RuntimeError("av is required to run this example, install with `pip install av`") + +# ensure LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET are set + +logger = logging.getLogger(__name__) + + +@dataclass +class MediaInfo: + video_width: int + video_height: int + video_fps: float + audio_sample_rate: int + audio_channels: int + + +class MediaFileStreamer: + """Streams video and audio frames from a media file in an endless loop.""" + + def __init__(self, media_file: Union[str, Path]) -> None: + self._media_file = str(media_file) + # Create separate containers for each stream + self._video_container = av.open(self._media_file) + self._audio_container = av.open(self._media_file) + + # Cache media info + video_stream = self._video_container.streams.video[0] + audio_stream = self._audio_container.streams.audio[0] + self._info = MediaInfo( + video_width=video_stream.width, + video_height=video_stream.height, + video_fps=float(video_stream.average_rate), # type: ignore + audio_sample_rate=audio_stream.sample_rate, + audio_channels=audio_stream.channels, + ) + + @property + def info(self) -> MediaInfo: + return self._info + + async def stream_video(self) -> AsyncIterable[tuple[rtc.VideoFrame, float]]: + """Streams video frames from the media file in an endless loop.""" + for i, av_frame in enumerate(self._video_container.decode(video=0)): + # Convert video frame to RGBA + frame = av_frame.to_rgb().to_ndarray() + frame_rgba = np.ones((frame.shape[0], frame.shape[1], 4), dtype=np.uint8) + frame_rgba[:, :, :3] = frame + yield ( + rtc.VideoFrame( + width=frame.shape[1], + height=frame.shape[0], + type=rtc.VideoBufferType.RGBA, + data=frame_rgba.tobytes(), + ), + av_frame.time, + ) + + async def stream_audio(self) -> AsyncIterable[tuple[rtc.AudioFrame, float]]: + """Streams audio frames from the media file in an endless loop.""" + for av_frame in self._audio_container.decode(audio=0): + # Convert audio frame to raw int16 samples + frame = av_frame.to_ndarray().T # Transpose to (samples, channels) + frame = (frame * 32768).astype(np.int16) + duration = len(frame) / self.info.audio_sample_rate + yield ( + rtc.AudioFrame( + data=frame.tobytes(), + sample_rate=self.info.audio_sample_rate, + num_channels=frame.shape[1], + samples_per_channel=frame.shape[0], + ), + av_frame.time + duration, + ) + + def reset(self): + self._video_container.seek(0) + self._audio_container.seek(0) + + async def aclose(self) -> None: + """Closes the media container and stops streaming.""" + self._video_container.close() + self._audio_container.close() + + +async def main(room: rtc.Room, room_name: str, media_path: str): + token = ( + api.AccessToken() + .with_identity("python-publisher") + .with_name("Python Publisher") + .with_grants( + api.VideoGrants( + room_join=True, + room=room_name, + agent=True, + ) + ) + .to_jwt() + ) + url = os.getenv("LIVEKIT_URL") + logging.info("connecting to %s", url) + + try: + await room.connect(url, token) + logging.info("connected to room %s", room.name) + except rtc.ConnectError as e: + logging.error("failed to connect to the room: %s", e) + return + + # Create media streamer + streamer = MediaFileStreamer(media_path) + media_info = streamer.info + + # Create video and audio sources/tracks + queue_size_ms = 1000 + video_source = rtc.VideoSource( + width=media_info.video_width, + height=media_info.video_height, + ) + logger.info(media_info) + audio_source = rtc.AudioSource( + sample_rate=media_info.audio_sample_rate, + num_channels=media_info.audio_channels, + queue_size_ms=queue_size_ms, + ) + + video_track = rtc.LocalVideoTrack.create_video_track("video", video_source) + audio_track = rtc.LocalAudioTrack.create_audio_track("audio", audio_source) + + # Publish tracks + video_options = rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + video_encoding=rtc.VideoEncoding( + max_framerate=30, + max_bitrate=5_000_000, + ), + ) + audio_options = rtc.TrackPublishOptions(source=rtc.TrackSource.SOURCE_MICROPHONE) + + await room.local_participant.publish_track(video_track, video_options) + await room.local_participant.publish_track(audio_track, audio_options) + + av_sync = rtc.AVSynchronizer( + audio_source=audio_source, + video_source=video_source, + video_fps=media_info.video_fps, + video_queue_size_ms=queue_size_ms, + ) + + async def _push_frames( + stream: AsyncIterable[tuple[rtc.VideoFrame | rtc.AudioFrame, float]], + av_sync: rtc.AVSynchronizer, + ): + async for frame, timestamp in stream: + await av_sync.push(frame, timestamp) + await asyncio.sleep(0) + + async def _log_fps(av_sync: rtc.AVSynchronizer): + start_time = asyncio.get_running_loop().time() + while True: + await asyncio.sleep(2) + wall_time = asyncio.get_running_loop().time() - start_time + diff = av_sync.last_video_time - av_sync.last_audio_time + logger.info( + f"fps: {av_sync.actual_fps:.2f}, wall_time: {wall_time:.3f}s, " + f"video_time: {av_sync.last_video_time:.3f}s, " + f"audio_time: {av_sync.last_audio_time:.3f}s, diff: {diff:.3f}s" + ) + + try: + while True: + streamer.reset() + + video_stream = streamer.stream_video() + audio_stream = streamer.stream_audio() + + # read the head frames and push them at the same time + first_video_frame, video_timestamp = await video_stream.__anext__() + first_audio_frame, audio_timestamp = await audio_stream.__anext__() + logger.info( + f"first video duration: {1 / media_info.video_fps:.3f}s, " + f"first audio duration: {first_audio_frame.duration:.3f}s" + ) + await av_sync.push(first_video_frame, video_timestamp) + await av_sync.push(first_audio_frame, audio_timestamp) + + video_task = asyncio.create_task(_push_frames(video_stream, av_sync)) + audio_task = asyncio.create_task(_push_frames(audio_stream, av_sync)) + + log_fps_task = asyncio.create_task(_log_fps(av_sync)) + + # wait for both tasks to complete + await asyncio.gather(video_task, audio_task) + await av_sync.wait_for_playout() + + # clean up + av_sync.reset() + log_fps_task.cancel() + logger.info("playout finished") + finally: + await streamer.aclose() + await av_sync.aclose() + await audio_source.aclose() + await video_source.aclose() + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, + handlers=[logging.FileHandler("video_play.log"), logging.StreamHandler()], + ) + + if len(sys.argv) != 3: + print("Usage: python video_play.py ") + sys.exit(1) + + room_name = sys.argv[1] + media_path = sys.argv[2] + + loop = asyncio.get_event_loop() + room = rtc.Room(loop=loop) + + async def cleanup(): + await room.disconnect() + loop.stop() + + asyncio.ensure_future(main(room, room_name, media_path)) + for signal in [signal.SIGINT, signal.SIGTERM]: + loop.add_signal_handler(signal, lambda: asyncio.ensure_future(cleanup())) + + try: + loop.run_forever() + finally: + loop.close() diff --git a/livekit-api/README.md b/livekit-api/README.md index c4b77002..18c5daba 100644 --- a/livekit-api/README.md +++ b/livekit-api/README.md @@ -1,3 +1,29 @@ # LiveKit Server APIs Access LiveKit server APIs and generate access tokens. + +See https://docs.livekit.io/reference/server/server-apis for more information. + +## Authentication + +Every request to the server APIs is authenticated. `LiveKitAPI` supports two modes: + +- **API key & secret** — recommended for backend use. The SDK signs a short-lived token per request from your key and secret. Keep your API secret on the server; never ship it to a client. +- **Access token** — for client-side use where the API secret must not be exposed. Pass a pre-signed [access token](https://docs.livekit.io/home/get-started/authentication/) that already carries the grants for the operations you'll perform; the SDK sends it verbatim. + +```python +from livekit import api + +# API key & secret: set LIVEKIT_URL, LIVEKIT_API_KEY, and LIVEKIT_API_SECRET, +# then construct with no arguments... +lkapi = api.LiveKitAPI() + +# ...or pass any of them explicitly to override the corresponding env var: +lkapi = api.LiveKitAPI("https://my.livekit.host", api_key="api-key", api_secret="api-secret") + +# Pre-signed access token (client-side): with LIVEKIT_URL set, pass just the token: +lkapi = api.LiveKitAPI.with_token("a-pre-signed-token") +``` + +The url and credentials fall back to the `LIVEKIT_URL`, `LIVEKIT_API_KEY`, `LIVEKIT_API_SECRET`, and `LIVEKIT_TOKEN` environment variables. Values you pass explicitly take precedence; the environment variables are used only as a fallback for arguments you omit — an ambient `LIVEKIT_TOKEN`, for example, won't override an explicitly-provided API key and secret. + diff --git a/livekit-api/livekit/api/__init__.py b/livekit-api/livekit/api/__init__.py index 714ce134..2af81a21 100644 --- a/livekit-api/livekit/api/__init__.py +++ b/livekit-api/livekit/api/__init__.py @@ -12,19 +12,67 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""LiveKit API SDK""" +"""LiveKit Server APIs for Python + +`pip install livekit-api` + +Manage rooms, participants, egress, ingress, SIP, and Agent dispatch. + +Primary entry point is `LiveKitAPI`. + +See https://docs.livekit.io/reference/server/server-apis for more information. +""" # flake8: noqa # re-export packages from protocol +from livekit.protocol.agent_dispatch import * +from livekit.protocol.agent import * from livekit.protocol.egress import * from livekit.protocol.ingress import * from livekit.protocol.models import * from livekit.protocol.room import * from livekit.protocol.webhook import * from livekit.protocol.sip import * +from livekit.protocol.connector_whatsapp import * +from livekit.protocol.connector_twilio import * -from .twirp_client import TwirpError, TwirpErrorCode +from .twirp_client import ( + ServerError, + ServerErrorCode, + SipCallError, + TwirpError, + TwirpErrorCode, +) from .livekit_api import LiveKitAPI -from .access_token import VideoGrants, SIPGrants, AccessToken, TokenVerifier +from .access_token import ( + InferenceGrants, + ObservabilityGrants, + VideoGrants, + SIPGrants, + AccessToken, + TokenVerifier, +) from .webhook import WebhookReceiver from .version import __version__ + +__all__ = [ + "LiveKitAPI", + "room_service", + "egress_service", + "ingress_service", + "sip_service", + "agent_dispatch_service", + "connector_service", + "InferenceGrants", + "ObservabilityGrants", + "VideoGrants", + "SIPGrants", + "AccessToken", + "TokenVerifier", + "WebhookReceiver", + "ServerError", + "ServerErrorCode", + "TwirpError", + "TwirpErrorCode", + "SipCallError", +] diff --git a/livekit-api/livekit/api/_dial_timeout.py b/livekit-api/livekit/api/_dial_timeout.py new file mode 100644 index 00000000..493afdb2 --- /dev/null +++ b/livekit-api/livekit/api/_dial_timeout.py @@ -0,0 +1,51 @@ +from __future__ import annotations + +from typing import Optional, Union + +from livekit.protocol.sip import CreateSIPParticipantRequest, TransferSIPParticipantRequest + +# Requests that carry ringing_timeout and share the phone-dialing timeout behavior. +DialRequest = Union[ + CreateSIPParticipantRequest, + TransferSIPParticipantRequest, +] +"""@private""" + +# Ring window (seconds) assumed when a request doesn't set ringing_timeout; +# matches the server default. A dialing request must outlast it. +DEFAULT_RINGING_TIMEOUT = 30.0 +"""@private""" + +# A dialing request must outlast the ringing window, or it would abort before +# the call can be answered. Keep the request timeout at least this many seconds +# above the ringing timeout. +RINGING_TIMEOUT_MARGIN = 2.0 +"""@private""" + + +def pin_ringing_timeout(request: DialRequest) -> None: + """Set the ring window explicitly on a dialing request when the caller left it + unset, so the derived request timeout doesn't depend on the server's default + (which could change out from under us). + + @private + """ + if not request.HasField("ringing_timeout"): + request.ringing_timeout.seconds = int(DEFAULT_RINGING_TIMEOUT) + + +def dial_timeout(user_timeout: Optional[float], request: DialRequest) -> float: + """Request timeout (seconds) for a phone-dialing call: the ring window plus a + margin, so the request doesn't abort before the call can be answered. The + ring window is the request's ringing_timeout when set, else + DEFAULT_RINGING_TIMEOUT. A longer user_timeout is honored; a shorter one is + raised to the floor. + + @private + """ + if request.HasField("ringing_timeout"): + ring: float = request.ringing_timeout.seconds + else: + ring = DEFAULT_RINGING_TIMEOUT + floor = ring + RINGING_TIMEOUT_MARGIN + return max(user_timeout if user_timeout else floor, floor) diff --git a/livekit-api/livekit/api/_failover.py b/livekit-api/livekit/api/_failover.py new file mode 100644 index 00000000..4babb74c --- /dev/null +++ b/livekit-api/livekit/api/_failover.py @@ -0,0 +1,162 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Region failover for the Twirp API clients. + +On a retryable failure (any transport error or HTTP 5xx) the client discovers +alternative LiveKit Cloud regions via ``/settings/regions`` and replays the +request against the next region, with exponential backoff. 4xx responses are +returned immediately. +""" + +from __future__ import annotations + +import time +from dataclasses import dataclass +from typing import Dict, List, Optional +from urllib.parse import urlparse + +import aiohttp + +FAILOVER_MAX_ATTEMPTS = 3 +FAILOVER_BACKOFF_BASE = 0.2 # seconds +# Below this per-request timeout (seconds) a retry is unlikely to help and many +# clients would retry in lockstep across regions, so a short request gets a +# single attempt (thundering-herd guard). +MIN_FAILOVER_TIMEOUT = 5.0 + + +def failover_attempts( + enabled: bool, + host: Optional[str], + force: bool = False, + timeout: Optional[float] = None, +) -> int: + """Total request attempts for a host; 1 means no failover. Failover only + engages when enabled, the host is a LiveKit Cloud domain, and the request + timeout is long enough to retry. ``force`` bypasses the cloud-host check and + is for internal testing only. + """ + if not (enabled and (force or (host is not None and is_cloud(host)))): + return 1 + if timeout is not None and 0 < timeout < MIN_FAILOVER_TIMEOUT: + return 1 + return FAILOVER_MAX_ATTEMPTS + + +def is_cloud(host: str) -> bool: + # Failover only engages for LiveKit Cloud project domains. + return host.endswith(".livekit.cloud") + + +def to_http(url: str) -> str: + """Normalizes a region URL to an http(s) scheme (ws -> http, wss -> https).""" + if url.startswith("ws"): + return "http" + url[2:] + return url + + +def origin_of(url: str) -> str: + """Returns the scheme://host[:port] origin of a URL, dropping any path.""" + parsed = urlparse(url) + return f"{parsed.scheme}://{parsed.netloc}" + + +def host_key(url: str) -> str: + """A stable key identifying a host (including port) for dedup across attempts.""" + return urlparse(url).netloc.lower() + + +def pick_next(region_origins: List[str], attempted: set[str]) -> Optional[str]: + """Returns the first region origin whose host has not yet been attempted.""" + for origin in region_origins: + if host_key(origin) not in attempted: + return origin + return None + + +@dataclass +class _CacheEntry: + origins: List[str] + fetched_at: float + ttl: float + + +class RegionCache: + """Process-wide cache of the LiveKit Cloud region list, keyed by host.""" + + def __init__(self) -> None: + self._entries: Dict[str, _CacheEntry] = {} + + async def region_origins( + self, + session: aiohttp.ClientSession, + origin: str, + headers: Dict[str, str], + ) -> List[str]: + """Returns alternative region origins for ``origin``, fetching + ``/settings/regions`` if the cache is stale. Best-effort: on a fetch + failure it serves a stale cached list when available, otherwise an empty + list. Forwards ``headers`` so a valid token — and any test directives — + reach the discovery endpoint.""" + key = host_key(origin) + entry = self._entries.get(key) + if entry is not None and (time.monotonic() - entry.fetched_at) < entry.ttl: + return entry.origins + + try: + origins, ttl = await self._fetch(session, origin, headers) + except Exception: + return entry.origins if entry is not None else [] + + # A zero TTL (e.g. Cache-Control: max-age=0) means "do not cache". + if ttl > 0: + self._entries[key] = _CacheEntry(origins, time.monotonic(), ttl) + return origins + + async def _fetch( + self, + session: aiohttp.ClientSession, + origin: str, + headers: Dict[str, str], + ) -> tuple[List[str], float]: + fetch_headers = { + k: v for k, v in headers.items() if k.lower() not in ("content-type", "content-length") + } + # Short timeout so a slow/unreachable discovery endpoint doesn't stall + # the failover path. + async with session.get( + f"{origin}/settings/regions", + headers=fetch_headers, + timeout=aiohttp.ClientTimeout(total=2), + ) as resp: + if resp.status != 200: + raise RuntimeError(f"region discovery failed: {resp.status}") + ttl = _parse_max_age(resp.headers.get("Cache-Control")) + body = await resp.json() + origins = [origin_of(to_http(r["url"])) for r in body.get("regions", []) if r.get("url")] + return origins, ttl + + +def _parse_max_age(cache_control: Optional[str]) -> float: + if not cache_control: + return 0.0 + for directive in cache_control.split(","): + directive = directive.strip().lower() + if directive.startswith("max-age="): + try: + return float(int(directive[len("max-age=") :])) + except ValueError: + return 0.0 + return 0.0 diff --git a/livekit-api/livekit/api/_service.py b/livekit-api/livekit/api/_service.py index 31527d00..6e3394aa 100644 --- a/livekit-api/livekit/api/_service.py +++ b/livekit-api/livekit/api/_service.py @@ -1,6 +1,5 @@ from __future__ import annotations -from typing import Dict import aiohttp from abc import ABC from .twirp_client import TwirpClient @@ -11,21 +10,32 @@ class Service(ABC): def __init__( - self, session: aiohttp.ClientSession, host: str, api_key: str, api_secret: str + self, + session: aiohttp.ClientSession, + host: str, + api_key: str, + api_secret: str, + failover: bool = True, ): - self._client = TwirpClient(session, host, "livekit") + self._client = TwirpClient(session, host, "livekit", failover=failover) self.api_key = api_key self.api_secret = api_secret + # A pre-signed token set by LiveKitAPI for token auth; sent verbatim, + # skipping per-call signing. Per-service constructors stay key/secret-only. + self._token: str | None = None def _auth_header( - self, grants: VideoGrants, sip: SIPGrants | None = None - ) -> Dict[str, str]: - tok = AccessToken(self.api_key, self.api_secret).with_grants(grants) + self, grants: VideoGrants | None, sip: SIPGrants | None = None + ) -> dict[str, str]: + # A pre-signed token is sent verbatim; the caller is responsible for its grants. + if self._token: + return {AUTHORIZATION: "Bearer {}".format(self._token)} + + tok = AccessToken(self.api_key, self.api_secret) + if grants: + tok.with_grants(grants) if sip is not None: - tok = tok.with_sip_grants(sip) + tok.with_sip_grants(sip) token = tok.to_jwt() - - headers = {} - headers[AUTHORIZATION] = "Bearer {}".format(token) - return headers + return {AUTHORIZATION: "Bearer {}".format(token)} diff --git a/livekit-api/livekit/api/access_token.py b/livekit-api/livekit/api/access_token.py index e840b9a5..e6516819 100644 --- a/livekit-api/livekit/api/access_token.py +++ b/livekit-api/livekit/api/access_token.py @@ -13,57 +13,66 @@ # limitations under the License. import calendar -import dataclasses import re import datetime import os import jwt -from typing import Optional, List +import dataclasses +from dataclasses import dataclass +from typing import Optional, List, Literal +from google.protobuf.json_format import MessageToDict, ParseDict + +from livekit.protocol.room import RoomConfiguration DEFAULT_TTL = datetime.timedelta(hours=6) DEFAULT_LEEWAY = datetime.timedelta(minutes=1) -@dataclasses.dataclass +@dataclass class VideoGrants: # actions on rooms - room_create: bool = False - room_list: bool = False - room_record: bool = False + room_create: Optional[bool] = None + room_list: Optional[bool] = None + room_record: Optional[bool] = None # actions on a particular room - room_admin: bool = False - room_join: bool = False + room_admin: Optional[bool] = None + room_join: Optional[bool] = None room: str = "" + # allows forwarding participant to room + destination_room: Optional[str] = None + # permissions within a room can_publish: bool = True can_subscribe: bool = True can_publish_data: bool = True # TrackSource types that a participant may publish. - # When set, it supercedes CanPublish. Only sources explicitly set here can be + # When set, it supersedes CanPublish. Only sources explicitly set here can be # published - can_publish_sources: List[str] = dataclasses.field(default_factory=list) + can_publish_sources: Optional[List[str]] = None # by default, a participant is not allowed to update its own metadata - can_update_own_metadata: bool = False + can_update_own_metadata: Optional[bool] = None # actions on ingresses - ingress_admin: bool = False # applies to all ingress + ingress_admin: Optional[bool] = None # applies to all ingress # participant is not visible to other participants (useful when making bots) - hidden: bool = False + hidden: Optional[bool] = None - # indicates to the room that current participant is a recorder - recorder: bool = False + # [deprecated] indicates to the room that current participant is a recorder + recorder: Optional[bool] = None # indicates that the holder can register as an Agent framework worker - # it is also set on all participants that are joining as Agent - agent: bool = False + agent: Optional[bool] = None + + # allow participant to manage an agent session via RemoteSession + can_manage_agent_session: Optional[bool] = None -@dataclasses.dataclass +@dataclass class SIPGrants: # manage sip resources admin: bool = False @@ -71,17 +80,49 @@ class SIPGrants: call: bool = False -@dataclasses.dataclass +@dataclass +class InferenceGrants: + # perform inference + perform: bool = False + + +@dataclass +class ObservabilityGrants: + # write grants to publish observability data + write: bool = False + + +@dataclass class Claims: identity: str = "" name: str = "" - video: VideoGrants = dataclasses.field(default_factory=VideoGrants) - sip: SIPGrants = dataclasses.field(default_factory=SIPGrants) + kind: str = "" metadata: str = "" - sha256: str = "" + video: Optional[VideoGrants] = None + sip: Optional[SIPGrants] = None + inference: Optional[InferenceGrants] = None + observability: Optional[ObservabilityGrants] = None + attributes: Optional[dict[str, str]] = None + sha256: Optional[str] = None + room_preset: Optional[str] = None + room_config: Optional[RoomConfiguration] = None + + def asdict(self) -> dict: + # in order to produce minimal jwt size, exclude None or empty values + claims = dataclasses.asdict( + self, + dict_factory=lambda items: { + snake_to_lower_camel(k): v for k, v in items if v is not None and v != "" + }, + ) + if self.room_config: + claims["roomConfig"] = MessageToDict(self.room_config) + return claims class AccessToken: + ParticipantKind = Literal["standard", "egress", "ingress", "sip", "agent"] + def __init__( self, api_key: Optional[str] = None, @@ -113,10 +154,22 @@ def with_sip_grants(self, grants: SIPGrants) -> "AccessToken": self.claims.sip = grants return self + def with_observability_grants(self, grants: ObservabilityGrants) -> "AccessToken": + self.claims.observability = grants + return self + + def with_inference_grants(self, grants: InferenceGrants) -> "AccessToken": + self.claims.inference = grants + return self + def with_identity(self, identity: str) -> "AccessToken": self.identity = identity return self + def with_kind(self, kind: ParticipantKind) -> "AccessToken": + self.claims.kind = kind + return self + def with_name(self, name: str) -> "AccessToken": self.claims.name = name return self @@ -125,31 +178,40 @@ def with_metadata(self, metadata: str) -> "AccessToken": self.claims.metadata = metadata return self + def with_attributes(self, attributes: dict[str, str]) -> "AccessToken": + self.claims.attributes = attributes + return self + def with_sha256(self, sha256: str) -> "AccessToken": self.claims.sha256 = sha256 return self + def with_room_preset(self, preset: str) -> "AccessToken": + self.claims.room_preset = preset + return self + + def with_room_config(self, config: RoomConfiguration) -> "AccessToken": + self.claims.room_config = config + return self + def to_jwt(self) -> str: video = self.claims.video - if video.room_join and (not self.identity or not video.room): + if video and video.room_join and (not self.identity or not video.room): raise ValueError("identity and room must be set when joining a room") - claims = dataclasses.asdict( - self.claims, - dict_factory=lambda items: {snake_to_lower_camel(k): v for k, v in items}, - ) - claims.update( + # we want to exclude None values from the token + jwt_claims = self.claims.asdict() + jwt_claims.update( { "sub": self.identity, "iss": self.api_key, - "nbf": calendar.timegm(datetime.datetime.utcnow().utctimetuple()), + "nbf": calendar.timegm(datetime.datetime.now(datetime.timezone.utc).utctimetuple()), "exp": calendar.timegm( - (datetime.datetime.utcnow() + self.ttl).utctimetuple() + (datetime.datetime.now(datetime.timezone.utc) + self.ttl).utctimetuple() ), } ) - - return jwt.encode(claims, self.api_secret, algorithm="HS256") + return jwt.encode(jwt_claims, self.api_secret, algorithm="HS256") class TokenVerifier: @@ -163,51 +225,66 @@ def __init__( api_key = api_key or os.getenv("LIVEKIT_API_KEY") api_secret = api_secret or os.getenv("LIVEKIT_API_SECRET") - if not api_key or not api_secret: - raise ValueError("api_key and api_secret must be set") - self.api_key = api_key self.api_secret = api_secret self._leeway = leeway - def verify(self, token: str) -> Claims: + def verify(self, token: str, *, verify_signature: bool = True) -> Claims: + if verify_signature and (not self.api_key or not self.api_secret): + raise ValueError("api_key and api_secret must be set") + claims = jwt.decode( token, - self.api_secret, - issuer=self.api_key, + key=self.api_secret or "", + issuer=self.api_key or "", algorithms=["HS256"], leeway=self._leeway.total_seconds(), + options={"verify_signature": verify_signature}, ) - video_dict = claims.get("video", dict()) video_dict = {camel_to_snake(k): v for k, v in video_dict.items()} - video_dict = { - k: v for k, v in video_dict.items() if k in VideoGrants.__dataclass_fields__ - } + video_dict = {k: v for k, v in video_dict.items() if k in VideoGrants.__dataclass_fields__} video = VideoGrants(**video_dict) sip_dict = claims.get("sip", dict()) sip_dict = {camel_to_snake(k): v for k, v in sip_dict.items()} - sip_dict = { - k: v for k, v in sip_dict.items() if k in SIPGrants.__dataclass_fields__ - } + sip_dict = {k: v for k, v in sip_dict.items() if k in SIPGrants.__dataclass_fields__} sip = SIPGrants(**sip_dict) - return Claims( + inference_dict = claims.get("inference", dict()) + inference_dict = {camel_to_snake(k): v for k, v in inference_dict.items()} + inference_dict = { + k: v for k, v in inference_dict.items() if k in InferenceGrants.__dataclass_fields__ + } + inference = InferenceGrants(**inference_dict) + + grant_claims = Claims( identity=claims.get("sub", ""), name=claims.get("name", ""), video=video, sip=sip, + inference=inference, + attributes=claims.get("attributes", {}), metadata=claims.get("metadata", ""), sha256=claims.get("sha256", ""), ) + if claims.get("roomPreset"): + grant_claims.room_preset = claims.get("roomPreset") + room_config = claims.get("roomConfig") + if room_config: + grant_claims.room_config = ParseDict( + room_config, + RoomConfiguration(), + ignore_unknown_fields=True, + ) + + return grant_claims + -def camel_to_snake(t: str): +def camel_to_snake(t: str) -> str: return re.sub(r"(? str: + return "".join(word.capitalize() if i else word for i, word in enumerate(t.split("_"))) diff --git a/livekit-api/livekit/api/agent_dispatch_service.py b/livekit-api/livekit/api/agent_dispatch_service.py new file mode 100644 index 00000000..d014e6d0 --- /dev/null +++ b/livekit-api/livekit/api/agent_dispatch_service.py @@ -0,0 +1,118 @@ +import aiohttp +from typing import Optional +from livekit.protocol.agent_dispatch import ( + CreateAgentDispatchRequest, + AgentDispatch, + DeleteAgentDispatchRequest, + ListAgentDispatchRequest, + ListAgentDispatchResponse, +) +from ._service import Service +from .access_token import VideoGrants + +SVC = "AgentDispatchService" +"""@private""" + + +class AgentDispatchService(Service): + """Manage agent dispatches. Service APIs require roomAdmin permissions. + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + agent_dispatch = lkapi.agent_dispatch + ``` + """ + + def __init__( + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, + ): + super().__init__(session, url, api_key, api_secret, failover=failover) + + async def create_dispatch(self, req: CreateAgentDispatchRequest) -> AgentDispatch: + """Create an explicit dispatch for an agent to join a room. + + To use explicit dispatch, your agent must be registered with an `agentName`. + + Args: + req (CreateAgentDispatchRequest): Request containing dispatch creation parameters. + The request can include an optional `deployment` field to target a specific + deployment. Leave empty to target the production deployment. + + Returns: + AgentDispatch: The created agent dispatch object + """ + return await self._client.request( + SVC, + "CreateDispatch", + req, + self._auth_header(VideoGrants(room_admin=True, room=req.room)), + AgentDispatch, + ) + + async def delete_dispatch(self, dispatch_id: str, room_name: str) -> AgentDispatch: + """Delete an explicit dispatch for an agent in a room. + + Args: + dispatch_id (str): ID of the dispatch to delete + room_name (str): Name of the room containing the dispatch + + Returns: + AgentDispatch: The deleted agent dispatch object + """ + return await self._client.request( + SVC, + "DeleteDispatch", + DeleteAgentDispatchRequest( + dispatch_id=dispatch_id, + room=room_name, + ), + self._auth_header(VideoGrants(room_admin=True, room=room_name)), + AgentDispatch, + ) + + async def list_dispatch(self, room_name: str) -> list[AgentDispatch]: + """List all agent dispatches in a room. + + Args: + room_name (str): Name of the room to list dispatches from + + Returns: + list[AgentDispatch]: List of agent dispatch objects in the room + """ + res = await self._client.request( + SVC, + "ListDispatch", + ListAgentDispatchRequest(room=room_name), + self._auth_header(VideoGrants(room_admin=True, room=room_name)), + ListAgentDispatchResponse, + ) + return list(res.agent_dispatches) + + async def get_dispatch(self, dispatch_id: str, room_name: str) -> Optional[AgentDispatch]: + """Get an Agent dispatch by ID + + Args: + dispatch_id (str): ID of the dispatch to retrieve + room_name (str): Name of the room containing the dispatch + + Returns: + Optional[AgentDispatch]: The requested agent dispatch object if found, None otherwise + """ + res = await self._client.request( + SVC, + "ListDispatch", + ListAgentDispatchRequest(dispatch_id=dispatch_id, room=room_name), + self._auth_header(VideoGrants(room_admin=True, room=room_name)), + ListAgentDispatchResponse, + ) + if len(res.agent_dispatches) > 0: + return res.agent_dispatches[0] + return None diff --git a/livekit-api/livekit/api/connector_service.py b/livekit-api/livekit/api/connector_service.py new file mode 100644 index 00000000..4f5c09c9 --- /dev/null +++ b/livekit-api/livekit/api/connector_service.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import aiohttp +from typing import Optional + +from livekit.protocol.connector_whatsapp import ( + DialWhatsAppCallRequest, + DialWhatsAppCallResponse, + DisconnectWhatsAppCallRequest, + DisconnectWhatsAppCallResponse, + ConnectWhatsAppCallRequest, + ConnectWhatsAppCallResponse, + AcceptWhatsAppCallRequest, + AcceptWhatsAppCallResponse, +) +from livekit.protocol.connector_twilio import ( + ConnectTwilioCallRequest, + ConnectTwilioCallResponse, +) +from ._service import Service +from ._dial_timeout import DEFAULT_RINGING_TIMEOUT +from .access_token import VideoGrants + +SVC = "Connector" +"""@private""" + + +class ConnectorService(Service): + """Client for LiveKit Connector Service API + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + connector_service = lkapi.connector + ``` + """ + + def __init__( + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, + ): + super().__init__(session, url, api_key, api_secret, failover=failover) + + async def dial_whatsapp_call( + self, request: DialWhatsAppCallRequest + ) -> DialWhatsAppCallResponse: + """ + Initiate an outbound WhatsApp call + + Args: + request: DialWhatsAppCallRequest containing call parameters + + Returns: + DialWhatsAppCallResponse with the WhatsApp call ID and room name + """ + return await self._client.request( + SVC, + "DialWhatsAppCall", + request, + self._auth_header(VideoGrants(room_create=True)), + DialWhatsAppCallResponse, + ) + + async def disconnect_whatsapp_call( + self, request: DisconnectWhatsAppCallRequest + ) -> DisconnectWhatsAppCallResponse: + """ + Disconnect an active WhatsApp call + + Args: + request: DisconnectWhatsAppCallRequest containing the call ID to disconnect + + Returns: + DisconnectWhatsAppCallResponse (empty response) + """ + return await self._client.request( + SVC, + "DisconnectWhatsAppCall", + request, + self._auth_header(VideoGrants(room_create=True)), + DisconnectWhatsAppCallResponse, + ) + + async def connect_whatsapp_call( + self, request: ConnectWhatsAppCallRequest + ) -> ConnectWhatsAppCallResponse: + """ + Connect a WhatsApp call with SDP information + + Args: + request: ConnectWhatsAppCallRequest containing call ID and SDP + + Returns: + ConnectWhatsAppCallResponse (empty response) + """ + return await self._client.request( + SVC, + "ConnectWhatsAppCall", + request, + self._auth_header(VideoGrants(room_create=True)), + ConnectWhatsAppCallResponse, + ) + + async def accept_whatsapp_call( + self, + request: AcceptWhatsAppCallRequest, + *, + timeout: Optional[float] = None, + ) -> AcceptWhatsAppCallResponse: + """ + Accept an inbound WhatsApp call + + Args: + request: AcceptWhatsAppCallRequest containing call parameters and SDP + timeout: Optional request timeout in seconds. When the request waits + for the inbound party to join (wait_until_answered), it defaults + to the standard ring window. + + Returns: + AcceptWhatsAppCallResponse with the room name + """ + client_timeout: Optional[aiohttp.ClientTimeout] = None + if request.wait_until_answered: + # Waiting for the inbound party to join can block, so default the + # request timeout to the standard ring window; the caller overrides + # via timeout. + client_timeout = aiohttp.ClientTimeout( + total=timeout if timeout else DEFAULT_RINGING_TIMEOUT + ) + elif timeout: + client_timeout = aiohttp.ClientTimeout(total=timeout) + + return await self._client.request( + SVC, + "AcceptWhatsAppCall", + request, + self._auth_header(VideoGrants(room_create=True)), + AcceptWhatsAppCallResponse, + timeout=client_timeout, + ) + + async def connect_twilio_call( + self, request: ConnectTwilioCallRequest + ) -> ConnectTwilioCallResponse: + """ + Connect a Twilio call to a LiveKit room + + Args: + request: ConnectTwilioCallRequest containing call parameters + + Returns: + ConnectTwilioCallResponse with the websocket URL for Twilio media stream + """ + return await self._client.request( + SVC, + "ConnectTwilioCall", + request, + self._auth_header(VideoGrants(room_create=True)), + ConnectTwilioCallResponse, + ) diff --git a/livekit-api/livekit/api/egress_service.py b/livekit-api/livekit/api/egress_service.py index a875459b..5a3065f2 100644 --- a/livekit-api/livekit/api/egress_service.py +++ b/livekit-api/livekit/api/egress_service.py @@ -1,112 +1,152 @@ import aiohttp -from livekit.protocol import egress as proto_egress +from livekit.protocol.egress import ( + RoomCompositeEgressRequest, + WebEgressRequest, + ParticipantEgressRequest, + TrackCompositeEgressRequest, + TrackEgressRequest, + UpdateLayoutRequest, + UpdateStreamRequest, + ListEgressRequest, + StopEgressRequest, + StartEgressRequest, + EgressInfo, + ListEgressResponse, +) from ._service import Service from .access_token import VideoGrants SVC = "Egress" +"""@private""" class EgressService(Service): + """Client for LiveKit Egress Service API + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + egress = lkapi.egress + ``` + + Also see https://docs.livekit.io/home/egress/overview/ + """ + def __init__( - self, session: aiohttp.ClientSession, url: str, api_key: str, api_secret: str + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, ): - super().__init__(session, url, api_key, api_secret) + super().__init__(session, url, api_key, api_secret, failover=failover) - async def start_room_composite_egress( - self, start: proto_egress.RoomCompositeEgressRequest - ) -> proto_egress.EgressInfo: + async def start_room_composite_egress(self, start: RoomCompositeEgressRequest) -> EgressInfo: + """Starts a composite recording of a room.""" return await self._client.request( SVC, "StartRoomCompositeEgress", start, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def start_web_egress( - self, start: proto_egress.WebEgressRequest - ) -> proto_egress.EgressInfo: + async def start_web_egress(self, start: WebEgressRequest) -> EgressInfo: + """Starts a recording of a web page.""" return await self._client.request( SVC, "StartWebEgress", start, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def start_participant_egress( - self, start: proto_egress.ParticipantEgressRequest - ) -> proto_egress.EgressInfo: + async def start_participant_egress(self, start: ParticipantEgressRequest) -> EgressInfo: + """Starts a recording of a participant.""" return await self._client.request( SVC, "StartParticipantEgress", start, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def start_track_composite_egress( - self, start: proto_egress.TrackCompositeEgressRequest - ) -> proto_egress.EgressInfo: + async def start_track_composite_egress(self, start: TrackCompositeEgressRequest) -> EgressInfo: + """Starts a composite recording with audio and video tracks.""" return await self._client.request( SVC, "StartTrackCompositeEgress", start, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def start_track_egress( - self, start: proto_egress.TrackEgressRequest - ) -> proto_egress.EgressInfo: + async def start_track_egress(self, start: TrackEgressRequest) -> EgressInfo: + """Starts a recording of a single track.""" return await self._client.request( SVC, "StartTrackEgress", start, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def update_layout( - self, update: proto_egress.UpdateLayoutRequest - ) -> proto_egress.EgressInfo: + async def start_egress(self, start: StartEgressRequest) -> EgressInfo: + """Starts an egress; the request's source and outputs determine the egress type.""" + return await self._client.request( + SVC, + "StartEgress", + start, + self._auth_header(VideoGrants(room_record=True)), + EgressInfo, + ) + + async def update_layout(self, update: UpdateLayoutRequest) -> EgressInfo: + """Updates the layout of a composite recording.""" return await self._client.request( SVC, "UpdateLayout", update, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def update_stream( - self, update: proto_egress.UpdateStreamRequest - ) -> proto_egress.EgressInfo: + async def update_stream(self, update: UpdateStreamRequest) -> EgressInfo: + """Updates the stream of a RoomComposite, Web, or Participant recording.""" return await self._client.request( SVC, "UpdateStream", update, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) - async def list_egress( - self, list: proto_egress.ListEgressRequest - ) -> proto_egress.ListEgressResponse: + async def list_egress(self, list: ListEgressRequest) -> ListEgressResponse: + """Lists all active egress and recently completed recordings. + + Args: + list (ListEgressRequest): arg contains optional filters: + - room_name: str - List all egresses for a specific room + - egress_id: str - Only list egress with matching ID + - active: bool - Only list active egresses + """ return await self._client.request( SVC, "ListEgress", list, self._auth_header(VideoGrants(room_record=True)), - proto_egress.ListEgressResponse, + ListEgressResponse, ) - async def stop_egress( - self, stop: proto_egress.StopEgressRequest - ) -> proto_egress.EgressInfo: + async def stop_egress(self, stop: StopEgressRequest) -> EgressInfo: + """Stops an active egress recording.""" return await self._client.request( SVC, "StopEgress", stop, self._auth_header(VideoGrants(room_record=True)), - proto_egress.EgressInfo, + EgressInfo, ) diff --git a/livekit-api/livekit/api/ingress_service.py b/livekit-api/livekit/api/ingress_service.py index abf691ef..b29ce460 100644 --- a/livekit-api/livekit/api/ingress_service.py +++ b/livekit-api/livekit/api/ingress_service.py @@ -1,57 +1,75 @@ import aiohttp -from livekit.protocol import ingress as proto_ingress +from livekit.protocol.ingress import ( + CreateIngressRequest, + IngressInfo, + UpdateIngressRequest, + ListIngressRequest, + DeleteIngressRequest, + ListIngressResponse, +) from ._service import Service from .access_token import VideoGrants SVC = "Ingress" +"""@private""" class IngressService(Service): + """Client for LiveKit Ingress Service API + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + ingress = lkapi.ingress + ``` + + Also see https://docs.livekit.io/home/ingress/overview/ + """ + def __init__( - self, session: aiohttp.ClientSession, url: str, api_key: str, api_secret: str + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, ): - super().__init__(session, url, api_key, api_secret) + super().__init__(session, url, api_key, api_secret, failover=failover) - async def create_ingress( - self, create: proto_ingress.CreateIngressRequest - ) -> proto_ingress.IngressInfo: + async def create_ingress(self, create: CreateIngressRequest) -> IngressInfo: return await self._client.request( SVC, "CreateIngress", create, self._auth_header(VideoGrants(ingress_admin=True)), - proto_ingress.IngressInfo, + IngressInfo, ) - async def update_ingress( - self, update: proto_ingress.UpdateIngressRequest - ) -> proto_ingress.IngressInfo: + async def update_ingress(self, update: UpdateIngressRequest) -> IngressInfo: return await self._client.request( SVC, "UpdateIngress", update, self._auth_header(VideoGrants(ingress_admin=True)), - proto_ingress.IngressInfo, + IngressInfo, ) - async def list_ingress( - self, list: proto_ingress.ListIngressRequest - ) -> proto_ingress.ListIngressResponse: + async def list_ingress(self, list: ListIngressRequest) -> ListIngressResponse: return await self._client.request( SVC, "ListIngress", list, self._auth_header(VideoGrants(ingress_admin=True)), - proto_ingress.ListIngressResponse, + ListIngressResponse, ) - async def delete_ingress( - self, delete: proto_ingress.DeleteIngressRequest - ) -> proto_ingress.IngressInfo: + async def delete_ingress(self, delete: DeleteIngressRequest) -> IngressInfo: return await self._client.request( SVC, "DeleteIngress", delete, self._auth_header(VideoGrants(ingress_admin=True)), - proto_ingress.IngressInfo, + IngressInfo, ) diff --git a/livekit-api/livekit/api/livekit_api.py b/livekit-api/livekit/api/livekit_api.py index efe00ff8..eec792f9 100644 --- a/livekit-api/livekit/api/livekit_api.py +++ b/livekit-api/livekit/api/livekit_api.py @@ -4,49 +4,167 @@ from .egress_service import EgressService from .ingress_service import IngressService from .sip_service import SipService -from typing import Optional +from .agent_dispatch_service import AgentDispatchService +from .connector_service import ConnectorService +from typing import Any, Optional class LiveKitAPI: + """LiveKit Server API Client + + This class is the main entrypoint, which exposes all services. + + Usage: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + rooms = await lkapi.room.list_rooms(api.proto_room.ListRoomsRequest(names=['test-room'])) + ``` + """ + def __init__( self, url: Optional[str] = None, api_key: Optional[str] = None, api_secret: Optional[str] = None, *, - timeout: aiohttp.ClientTimeout = aiohttp.ClientTimeout(total=60), # 60 seconds + token: Optional[str] = None, + timeout: Optional[aiohttp.ClientTimeout] = None, + session: Optional[aiohttp.ClientSession] = None, + failover: bool = True, ): + """Create a new LiveKitAPI instance. + + Authenticate with an API key and secret (recommended for backend use). + For token auth (client-side use, where the API secret must not be + exposed), prefer the :meth:`with_token` constructor. + + Args: + url: LiveKit server URL (read from `LIVEKIT_URL` environment variable if not provided) + api_key: API key (read from `LIVEKIT_API_KEY` environment variable if not provided) + api_secret: API secret (read from `LIVEKIT_API_SECRET` environment variable if not provided) + token: Pre-signed access token (read from `LIVEKIT_TOKEN` environment variable if not provided) + timeout: Request timeout (default: 10 seconds) + session: aiohttp.ClientSession instance to use for requests, if not provided, a new one will be created + """ url = url or os.getenv("LIVEKIT_URL") - api_key = api_key or os.getenv("LIVEKIT_API_KEY") - api_secret = api_secret or os.getenv("LIVEKIT_API_SECRET") + + # Only fall back to environment credentials when none were provided + # explicitly, so an ambient LIVEKIT_TOKEN can't silently override an + # explicit api_key/secret (or vice versa). + if not token and not api_key and not api_secret: + token = os.getenv("LIVEKIT_TOKEN") + if not token and not api_key and not api_secret: + api_key = os.getenv("LIVEKIT_API_KEY") + api_secret = os.getenv("LIVEKIT_API_SECRET") if not url: raise ValueError("url must be set") - if not api_key or not api_secret: - raise ValueError("api_key and api_secret must be set") + if not token and (not api_key or not api_secret): + raise ValueError("either token, or api_key and api_secret, must be set") + + self._custom_session = True + self._session = session + if not self._session: + self._custom_session = False + if not timeout: + timeout = aiohttp.ClientTimeout(total=10) + self._session = aiohttp.ClientSession(timeout=timeout) + + # In token mode there is no key/secret; pass empty strings and rely on + # the token, injected into each service below. + key = api_key or "" + secret = api_secret or "" + self._room = RoomService(self._session, url, key, secret, failover) + self._ingress = IngressService(self._session, url, key, secret, failover) + self._egress = EgressService(self._session, url, key, secret, failover) + self._sip = SipService(self._session, url, key, secret, failover) + self._agent_dispatch = AgentDispatchService(self._session, url, key, secret, failover) + self._connector = ConnectorService(self._session, url, key, secret, failover) - self._session = aiohttp.ClientSession(timeout=timeout) - self._room = RoomService(self._session, url, api_key, api_secret) - self._ingress = IngressService(self._session, url, api_key, api_secret) - self._egress = EgressService(self._session, url, api_key, api_secret) - self._sip = SipService(self._session, url, api_key, api_secret) + if token: + for svc in ( + self._room, + self._ingress, + self._egress, + self._sip, + self._agent_dispatch, + self._connector, + ): + svc._token = token + + @classmethod + def with_token( + cls, + token: str, + url: Optional[str] = None, + *, + timeout: Optional[aiohttp.ClientTimeout] = None, + session: Optional[aiohttp.ClientSession] = None, + failover: bool = True, + ) -> "LiveKitAPI": + """Create a LiveKitAPI authenticated with a pre-signed token. + + The token is sent verbatim and must already carry the grants for the calls + it's used with. Since it needs no secret, this is suitable for client-side + use. `url` falls back to the `LIVEKIT_URL` environment variable. + + Args: + token: Pre-signed access token + url: LiveKit server URL (read from `LIVEKIT_URL` if not provided) + timeout: Request timeout (default: 10 seconds) + session: aiohttp.ClientSession to use; a new one is created if omitted + """ + return cls(url, token=token, timeout=timeout, session=session, failover=failover) + + @property + def agent_dispatch(self) -> AgentDispatchService: + """Instance of the AgentDispatchService""" + return self._agent_dispatch @property - def room(self): + def room(self) -> RoomService: + """Instance of the RoomService""" return self._room @property - def ingress(self): + def ingress(self) -> IngressService: + """Instance of the IngressService""" return self._ingress @property - def egress(self): + def egress(self) -> EgressService: + """Instance of the EgressService""" return self._egress @property - def sip(self): + def sip(self) -> SipService: + """Instance of the SipService""" return self._sip - async def aclose(self): - await self._session.close() + @property + def connector(self) -> ConnectorService: + """Instance of the ConnectorService""" + return self._connector + + async def aclose(self) -> None: + """Close the API client + + Call this before your application exits or when the API client is no longer needed.""" + # we do not close custom sessions, that's up to the caller + if not self._custom_session and self._session is not None: + await self._session.close() + + async def __aenter__(self) -> "LiveKitAPI": + """@private + + Support for `async with`""" + return self + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + """@private + + Support for `async with`""" + await self.aclose() diff --git a/livekit-api/livekit/api/room_service.py b/livekit-api/livekit/api/room_service.py index 9c1b197a..e3cb7bb6 100644 --- a/livekit-api/livekit/api/room_service.py +++ b/livekit-api/livekit/api/room_service.py @@ -1,136 +1,355 @@ import aiohttp -from livekit.protocol import room as proto_room -from livekit.protocol import models as proto_models +from uuid import uuid4 +from livekit.protocol.room import ( + CreateRoomRequest, + ListRoomsRequest, + DeleteRoomRequest, + ListRoomsResponse, + DeleteRoomResponse, + ListParticipantsRequest, + ListParticipantsResponse, + RoomParticipantIdentity, + MuteRoomTrackRequest, + MuteRoomTrackResponse, + UpdateParticipantRequest, + UpdateSubscriptionsRequest, + SendDataRequest, + SendDataResponse, + UpdateRoomMetadataRequest, + RemoveParticipantResponse, + UpdateSubscriptionsResponse, + ForwardParticipantRequest, + ForwardParticipantResponse, + MoveParticipantRequest, + MoveParticipantResponse, +) +from livekit.protocol.models import Room, ParticipantInfo from ._service import Service from .access_token import VideoGrants SVC = "RoomService" +"""@private""" class RoomService(Service): + """Client for LiveKit RoomService API + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + room_service = lkapi.room + ``` + + Also see https://docs.livekit.io/home/server/managing-rooms/ and https://docs.livekit.io/home/server/managing-participants/ + """ + def __init__( - self, session: aiohttp.ClientSession, url: str, api_key: str, api_secret: str + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, ): - super().__init__(session, url, api_key, api_secret) + super().__init__(session, url, api_key, api_secret, failover=failover) async def create_room( - self, create: proto_room.CreateRoomRequest - ) -> proto_models.Room: + self, + create: CreateRoomRequest, + ) -> Room: + """Creates a new room with specified configuration. + + Args: + create (CreateRoomRequest): arg containing: + - name: str - Unique room name + - empty_timeout: int - Seconds to keep room open if empty + - max_participants: int - Max allowed participants + - metadata: str - Custom room metadata + - egress: RoomEgress - Egress configuration + - min_playout_delay: int - Minimum playout delay in ms + - max_playout_delay: int - Maximum playout delay in ms + - sync_streams: bool - Enable A/V sync for playout delays >200ms + + Returns: + Room: The created room object + """ return await self._client.request( SVC, "CreateRoom", create, self._auth_header(VideoGrants(room_create=True)), - proto_models.Room, + Room, ) - async def list_rooms( - self, list: proto_room.ListRoomsRequest - ) -> proto_room.ListRoomsResponse: + async def list_rooms(self, list: ListRoomsRequest) -> ListRoomsResponse: + """Lists active rooms. + + Args: + list (ListRoomsRequest): arg containing: + - names: list[str] - Optional list of room names to filter by + + Returns: + ListRoomsResponse: + - rooms: list[Room] - List of active Room objects + """ return await self._client.request( SVC, "ListRooms", list, self._auth_header(VideoGrants(room_list=True)), - proto_room.ListRoomsResponse, + ListRoomsResponse, ) - async def delete_room( - self, delete: proto_room.DeleteRoomRequest - ) -> proto_room.DeleteRoomResponse: + async def delete_room(self, delete: DeleteRoomRequest) -> DeleteRoomResponse: + """Deletes a room and disconnects all participants. + + Args: + delete (DeleteRoomRequest): arg containing: + - room: str - Name of room to delete + + Returns: + DeleteRoomResponse: Empty response object + """ return await self._client.request( SVC, "DeleteRoom", delete, self._auth_header(VideoGrants(room_create=True)), - proto_room.DeleteRoomResponse, + DeleteRoomResponse, ) - async def update_room_metadata( - self, update: proto_room.UpdateRoomMetadataRequest - ) -> proto_models.Room: + async def update_room_metadata(self, update: UpdateRoomMetadataRequest) -> Room: + """Updates a room's [metadata](https://docs.livekit.io/home/client/data/room-metadata/). + + Args: + update (UpdateRoomMetadataRequest): arg containing: + - room: str - Name of room to update + - metadata: str - New metadata to set + + Returns: + Room: Updated Room object + """ return await self._client.request( SVC, "UpdateRoomMetadata", update, self._auth_header(VideoGrants(room_admin=True, room=update.room)), - proto_models.Room, + Room, ) - async def list_participants( - self, list: proto_room.ListParticipantsRequest - ) -> proto_room.ListParticipantsResponse: + async def list_participants(self, list: ListParticipantsRequest) -> ListParticipantsResponse: + """Lists all participants in a room. + + Args: + list (ListParticipantsRequest): arg containing: + - room: str - Name of room to list participants from + + Returns: + ListParticipantsResponse: + - participants: list[ParticipantInfo] - List of participant details + """ return await self._client.request( SVC, "ListParticipants", list, self._auth_header(VideoGrants(room_admin=True, room=list.room)), - proto_room.ListParticipantsResponse, + ListParticipantsResponse, ) - async def get_participant( - self, get: proto_room.RoomParticipantIdentity - ) -> proto_models.ParticipantInfo: + async def get_participant(self, get: RoomParticipantIdentity) -> ParticipantInfo: + """Gets details about a specific participant. + + Args: + get (RoomParticipantIdentity): arg containing: + - room: str - Room name + - identity: str - Participant identity to look up + + Returns: + ParticipantInfo: + - sid: str - Participant session ID + - identity: str - Participant identity + - state: int - Connection state + - tracks: list[TrackInfo] - Published tracks + - metadata: str - Participant metadata + - joined_at: int - Join timestamp + - name: str - Display name + - version: int - Protocol version + - permission: ParticipantPermission - Granted permissions + - region: str - Connected region + """ return await self._client.request( SVC, "GetParticipant", get, self._auth_header(VideoGrants(room_admin=True, room=get.room)), - proto_models.ParticipantInfo, + ParticipantInfo, ) async def remove_participant( - self, remove: proto_room.RoomParticipantIdentity - ) -> proto_room.RemoveParticipantResponse: + self, remove: RoomParticipantIdentity + ) -> RemoveParticipantResponse: + """Removes a participant from a room. + + Args: + remove (RoomParticipantIdentity): arg containing: + - room: str - Room name + - identity: str - Identity of participant to remove + + Returns: + RemoveParticipantResponse: Empty response object + """ return await self._client.request( SVC, "RemoveParticipant", remove, self._auth_header(VideoGrants(room_admin=True, room=remove.room)), - proto_room.RemoveParticipantResponse, + RemoveParticipantResponse, + ) + + async def forward_participant(self, forward: ForwardParticipantRequest) -> None: + """Forwards a participant and their published tracks from one room to another. + + This feature is only available for LiveKit Cloud/Private Cloud. + + Args: + forward (ForwardParticipantRequest): arg containing: + - room: str - Room name + - identity: str - identity of Participant to forward + - destination_room: str - Destination room name + """ + # currently nothing is returned + await self._client.request( + SVC, + "ForwardParticipant", + forward, + self._auth_header( + VideoGrants( + room_admin=True, room=forward.room, destination_room=forward.destination_room + ) + ), + ForwardParticipantResponse, + ) + + async def move_participant(self, move: MoveParticipantRequest) -> None: + """Moves a participant from one room to another. + + This feature is only available for LiveKit Cloud/Private Cloud. + + Args: + move (MoveParticipantRequest): arg containing: + - room: str - Room name + - identity: str - Participant identity + - destination_room: str - Destination room name + """ + # currently nothing is returned + await self._client.request( + SVC, + "MoveParticipant", + move, + self._auth_header( + VideoGrants( + room_admin=True, + room=move.room, + destination_room=move.destination_room, + ) + ), + MoveParticipantResponse, ) async def mute_published_track( self, - update: proto_room.MuteRoomTrackRequest, - ) -> proto_room.MuteRoomTrackResponse: + update: MuteRoomTrackRequest, + ) -> MuteRoomTrackResponse: + """Mutes or unmutes a participant's published track. + + Args: + update (MuteRoomTrackRequest): arg containing: + - room: str - Room name + - identity: str - Participant identity + - track_sid: str - Track session ID to mute + - muted: bool - True to mute, False to unmute + + Returns: + MuteRoomTrackResponse containing: + - track: TrackInfo - Updated track information + """ return await self._client.request( SVC, "MutePublishedTrack", update, self._auth_header(VideoGrants(room_admin=True, room=update.room)), - proto_room.MuteRoomTrackResponse, + MuteRoomTrackResponse, ) - async def update_participant( - self, update: proto_room.UpdateParticipantRequest - ) -> proto_models.ParticipantInfo: + async def update_participant(self, update: UpdateParticipantRequest) -> ParticipantInfo: + """Updates a participant's metadata or permissions. + + Args: + update (UpdateParticipantRequest): arg containing: + - room: str - Room name + - identity: str - Participant identity + - metadata: str - New metadata + - permission: ParticipantPermission - New permissions + - name: str - New display name + - attributes: dict[str, str] - Key-value attributes + + Returns: + ParticipantInfo: Updated participant information + """ return await self._client.request( SVC, "UpdateParticipant", update, self._auth_header(VideoGrants(room_admin=True, room=update.room)), - proto_models.ParticipantInfo, + ParticipantInfo, ) async def update_subscriptions( - self, update: proto_room.UpdateSubscriptionsRequest - ) -> proto_room.UpdateSubscriptionsResponse: + self, update: UpdateSubscriptionsRequest + ) -> UpdateSubscriptionsResponse: + """Updates a participant's track subscriptions. + + Args: + update (UpdateSubscriptionsRequest): arg containing: + - room: str - Room name + - identity: str - Participant identity + - track_sids: list[str] - Track session IDs + - subscribe: bool - True to subscribe, False to unsubscribe + - participant_tracks: list[ParticipantTracks] - Participant track mappings + + Returns: + UpdateSubscriptionsResponse: Empty response object + """ return await self._client.request( SVC, "UpdateSubscriptions", update, self._auth_header(VideoGrants(room_admin=True, room=update.room)), - proto_room.UpdateSubscriptionsResponse, + UpdateSubscriptionsResponse, ) - async def send_data( - self, send: proto_room.SendDataRequest - ) -> proto_room.SendDataResponse: + async def send_data(self, send: SendDataRequest) -> SendDataResponse: + """Sends data to participants in a room. + + Args: + send (SendDataRequest): arg containing: + - room: str - Room name + - data: bytes - Data payload to send + - kind: DataPacket.Kind - RELIABLE or LOSSY delivery + - destination_identities: list[str] - Target participant identities + - topic: str - Optional topic for the message + + Returns: + SendDataResponse: Empty response object + """ + + send.nonce = uuid4().bytes return await self._client.request( SVC, "SendData", send, self._auth_header(VideoGrants(room_admin=True, room=send.room)), - proto_room.SendDataResponse, + SendDataResponse, ) diff --git a/livekit-api/livekit/api/sip_service.py b/livekit-api/livekit/api/sip_service.py index 80810528..0b46cf20 100644 --- a/livekit-api/livekit/api/sip_service.py +++ b/livekit-api/livekit/api/sip_service.py @@ -1,134 +1,871 @@ +from __future__ import annotations + import aiohttp -from livekit.protocol import sip as proto_sip +import warnings +from typing import Optional + +from livekit.protocol.models import ListUpdate +from livekit.protocol.sip import ( + SIPOutboundConfig, + SIPTrunkInfo, + CreateSIPInboundTrunkRequest, + UpdateSIPInboundTrunkRequest, + SIPInboundTrunkInfo, + SIPInboundTrunkUpdate, + CreateSIPOutboundTrunkRequest, + UpdateSIPOutboundTrunkRequest, + SIPOutboundTrunkInfo, + SIPOutboundTrunkUpdate, + ListSIPInboundTrunkRequest, + ListSIPInboundTrunkResponse, + ListSIPOutboundTrunkRequest, + ListSIPOutboundTrunkResponse, + DeleteSIPTrunkRequest, + SIPDispatchRule, + SIPDispatchRuleInfo, + SIPDispatchRuleUpdate, + CreateSIPDispatchRuleRequest, + UpdateSIPDispatchRuleRequest, + ListSIPDispatchRuleRequest, + ListSIPDispatchRuleResponse, + DeleteSIPDispatchRuleRequest, + CreateSIPParticipantRequest, + TransferSIPParticipantRequest, + SIPParticipantInfo, + SIPTransport, + SIPMediaConfig, +) from ._service import Service +from .twirp_client import SipCallError, ServerError +from ._dial_timeout import ( + dial_timeout as _dial_timeout, + pin_ringing_timeout as _pin_ringing_timeout, +) from .access_token import VideoGrants, SIPGrants SVC = "SIP" +"""@private""" + + +def _as_sip_error(err: ServerError) -> ServerError: + """Surface a SIP dialing failure as a SipCallError so callers can branch on + the SIP status; other failures (auth, validation) are returned unchanged.""" + if "sip_status_code" in err.metadata: + return SipCallError.from_server_error(err) + return err class SipService(Service): + """Client for LiveKit SIP Service API + + Recommended way to use this service is via `livekit.api.LiveKitAPI`: + + ```python + from livekit import api + lkapi = api.LiveKitAPI() + sip_service = lkapi.sip + ``` + """ + def __init__( - self, session: aiohttp.ClientSession, url: str, api_key: str, api_secret: str + self, + session: aiohttp.ClientSession, + url: str, + api_key: str, + api_secret: str, + failover: bool = True, ): - super().__init__(session, url, api_key, api_secret) + super().__init__(session, url, api_key, api_secret, failover=failover) + + async def create_inbound_trunk( + self, create: CreateSIPInboundTrunkRequest + ) -> SIPInboundTrunkInfo: + """Create a new SIP inbound trunk. - async def create_sip_trunk( - self, create: proto_sip.CreateSIPTrunkRequest - ) -> proto_sip.SIPTrunkInfo: + Args: + create: Request containing trunk details + + Returns: + Created SIP inbound trunk + """ return await self._client.request( SVC, - "CreateSIPTrunk", + "CreateSIPInboundTrunk", create, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPTrunkInfo, + self._admin_headers(), + SIPInboundTrunkInfo, ) async def create_sip_inbound_trunk( - self, create: proto_sip.CreateSIPInboundTrunkRequest - ) -> proto_sip.SIPInboundTrunkInfo: + self, create: CreateSIPInboundTrunkRequest + ) -> SIPInboundTrunkInfo: + """Create a new SIP inbound trunk. + + .. deprecated:: + Use :meth:`create_inbound_trunk` instead. + + Args: + create: Request containing trunk details + + Returns: + Created SIP inbound trunk + """ + warnings.warn( + "create_sip_inbound_trunk is deprecated, use create_inbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.create_inbound_trunk(create) + + async def update_inbound_trunk( + self, + trunk_id: str, + trunk: SIPInboundTrunkInfo, + ) -> SIPInboundTrunkInfo: + """Updates an existing SIP inbound trunk by replacing it entirely. + + Args: + trunk_id: ID of the SIP inbound trunk to update + trunk: SIP inbound trunk to update with + + Returns: + Updated SIP inbound trunk + """ return await self._client.request( SVC, - "CreateSIPInboundTrunk", - create, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPInboundTrunkInfo, + "UpdateSIPInboundTrunk", + UpdateSIPInboundTrunkRequest( + sip_trunk_id=trunk_id, + replace=trunk, + ), + self._admin_headers(), + SIPInboundTrunkInfo, ) - async def create_sip_outbound_trunk( - self, create: proto_sip.CreateSIPOutboundTrunkRequest - ) -> proto_sip.SIPOutboundTrunkInfo: + async def update_sip_inbound_trunk( + self, + trunk_id: str, + trunk: SIPInboundTrunkInfo, + ) -> SIPInboundTrunkInfo: + """Updates an existing SIP inbound trunk by replacing it entirely. + + .. deprecated:: + Use :meth:`update_inbound_trunk` instead. + + Args: + trunk_id: ID of the SIP inbound trunk to update + trunk: SIP inbound trunk to update with + + Returns: + Updated SIP inbound trunk + """ + warnings.warn( + "update_sip_inbound_trunk is deprecated, use update_inbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_inbound_trunk(trunk_id, trunk) + + async def update_inbound_trunk_fields( + self, + trunk_id: str, + *, + numbers: Optional[ListUpdate | list[str]] = None, + allowed_addresses: Optional[ListUpdate | list[str]] = None, + allowed_numbers: Optional[ListUpdate | list[str]] = None, + auth_username: Optional[str] = None, + auth_password: Optional[str] = None, + name: Optional[str] = None, + metadata: Optional[str] = None, + media: Optional[SIPMediaConfig] = None, + ) -> SIPInboundTrunkInfo: + """Updates specific fields of an existing SIP inbound trunk. + + Only provided fields will be updated. + """ + update = SIPInboundTrunkUpdate( + auth_username=auth_username, + auth_password=auth_password, + name=name, + metadata=metadata, + media=media, + ) + if numbers is not None: + if isinstance(numbers, ListUpdate): + update.numbers.set.extend(numbers.set) + update.numbers.add.extend(numbers.add) + update.numbers.remove.extend(numbers.remove) + else: + update.numbers.set.extend(numbers) + if allowed_addresses is not None: + if isinstance(allowed_addresses, ListUpdate): + update.allowed_addresses.set.extend(allowed_addresses.set) + update.allowed_addresses.add.extend(allowed_addresses.add) + update.allowed_addresses.remove.extend(allowed_addresses.remove) + else: + update.allowed_addresses.set.extend(allowed_addresses) + if allowed_numbers is not None: + if isinstance(allowed_numbers, ListUpdate): + update.allowed_numbers.set.extend(allowed_numbers.set) + update.allowed_numbers.add.extend(allowed_numbers.add) + update.allowed_numbers.remove.extend(allowed_numbers.remove) + else: + update.allowed_numbers.set.extend(allowed_numbers) + + return await self._client.request( + SVC, + "UpdateSIPInboundTrunk", + UpdateSIPInboundTrunkRequest( + sip_trunk_id=trunk_id, + update=update, + ), + self._admin_headers(), + SIPInboundTrunkInfo, + ) + + async def update_sip_inbound_trunk_fields( + self, + trunk_id: str, + *, + numbers: Optional[list[str]] = None, + allowed_addresses: Optional[list[str]] = None, + allowed_numbers: Optional[list[str]] = None, + auth_username: Optional[str] = None, + auth_password: Optional[str] = None, + name: Optional[str] = None, + metadata: Optional[str] = None, + ) -> SIPInboundTrunkInfo: + """Updates specific fields of an existing SIP inbound trunk. + + .. deprecated:: + Use :meth:`update_inbound_trunk_fields` instead. + + Only provided fields will be updated. + """ + warnings.warn( + "update_sip_inbound_trunk_fields is deprecated, use update_inbound_trunk_fields instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_inbound_trunk_fields( + trunk_id, + numbers=numbers, + allowed_addresses=allowed_addresses, + allowed_numbers=allowed_numbers, + auth_username=auth_username, + auth_password=auth_password, + name=name, + metadata=metadata, + ) + + async def create_outbound_trunk( + self, create: CreateSIPOutboundTrunkRequest + ) -> SIPOutboundTrunkInfo: + """Create a new SIP outbound trunk. + + Args: + create: Request containing trunk details + + Returns: + Created SIP outbound trunk + """ return await self._client.request( SVC, "CreateSIPOutboundTrunk", create, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPOutboundTrunkInfo, + self._admin_headers(), + SIPOutboundTrunkInfo, ) - async def list_sip_trunk( - self, list: proto_sip.ListSIPTrunkRequest - ) -> proto_sip.ListSIPTrunkResponse: + async def create_sip_outbound_trunk( + self, create: CreateSIPOutboundTrunkRequest + ) -> SIPOutboundTrunkInfo: + """Create a new SIP outbound trunk. + + .. deprecated:: + Use :meth:`create_outbound_trunk` instead. + + Args: + create: Request containing trunk details + + Returns: + Created SIP outbound trunk + """ + warnings.warn( + "create_sip_outbound_trunk is deprecated, use create_outbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.create_outbound_trunk(create) + + async def update_outbound_trunk( + self, + trunk_id: str, + trunk: SIPOutboundTrunkInfo, + ) -> SIPOutboundTrunkInfo: + """Updates an existing SIP outbound trunk by replacing it entirely. + + Args: + trunk_id: ID of the SIP outbound trunk to update + trunk: SIP outbound trunk to update with + + Returns: + Updated SIP outbound trunk + """ return await self._client.request( SVC, - "ListSIPTrunk", - list, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.ListSIPTrunkResponse, + "UpdateSIPOutboundTrunk", + UpdateSIPOutboundTrunkRequest( + sip_trunk_id=trunk_id, + replace=trunk, + ), + self._admin_headers(), + SIPOutboundTrunkInfo, ) - async def list_sip_inbound_trunk( - self, list: proto_sip.ListSIPInboundTrunkRequest - ) -> proto_sip.ListSIPInboundTrunkResponse: + async def update_sip_outbound_trunk( + self, + trunk_id: str, + trunk: SIPOutboundTrunkInfo, + ) -> SIPOutboundTrunkInfo: + """Updates an existing SIP outbound trunk by replacing it entirely. + + .. deprecated:: + Use :meth:`update_outbound_trunk` instead. + + Args: + trunk_id: ID of the SIP outbound trunk to update + trunk: SIP outbound trunk to update with + + Returns: + Updated SIP outbound trunk + """ + warnings.warn( + "update_sip_outbound_trunk is deprecated, use update_outbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_outbound_trunk(trunk_id, trunk) + + async def update_outbound_trunk_fields( + self, + trunk_id: str, + *, + address: str | None = None, + transport: SIPTransport | None = None, + numbers: Optional[ListUpdate | list[str]] = None, + auth_username: str | None = None, + auth_password: str | None = None, + name: str | None = None, + metadata: str | None = None, + media: Optional[SIPMediaConfig] = None, + ) -> SIPOutboundTrunkInfo: + """Updates specific fields of an existing SIP outbound trunk. + + Only provided fields will be updated. + """ + update = SIPOutboundTrunkUpdate( + address=address, + transport=transport, + auth_username=auth_username, + auth_password=auth_password, + name=name, + metadata=metadata, + media=media, + ) + if numbers is not None: + if isinstance(numbers, ListUpdate): + update.numbers.set.extend(numbers.set) + update.numbers.add.extend(numbers.add) + update.numbers.remove.extend(numbers.remove) + else: + update.numbers.set.extend(numbers) + + return await self._client.request( + SVC, + "UpdateSIPOutboundTrunk", + UpdateSIPOutboundTrunkRequest( + sip_trunk_id=trunk_id, + update=update, + ), + self._admin_headers(), + SIPOutboundTrunkInfo, + ) + + async def update_sip_outbound_trunk_fields( + self, + trunk_id: str, + *, + address: str | None = None, + transport: SIPTransport | None = None, + numbers: list[str] | None = None, + auth_username: str | None = None, + auth_password: str | None = None, + name: str | None = None, + metadata: str | None = None, + ) -> SIPOutboundTrunkInfo: + """Updates specific fields of an existing SIP outbound trunk. + + .. deprecated:: + Use :meth:`update_outbound_trunk_fields` instead. + + Only provided fields will be updated. + """ + warnings.warn( + "update_sip_outbound_trunk_fields is deprecated, use update_outbound_trunk_fields instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_outbound_trunk_fields( + trunk_id, + address=address, + transport=transport, + numbers=numbers, + auth_username=auth_username, + auth_password=auth_password, + name=name, + metadata=metadata, + ) + + async def list_inbound_trunk( + self, list: ListSIPInboundTrunkRequest + ) -> ListSIPInboundTrunkResponse: + """List SIP inbound trunks with optional filtering. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP inbound trunks + """ return await self._client.request( SVC, "ListSIPInboundTrunk", list, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.ListSIPInboundTrunkResponse, + self._admin_headers(), + ListSIPInboundTrunkResponse, ) - async def list_sip_outbound_trunk( - self, list: proto_sip.ListSIPOutboundTrunkRequest - ) -> proto_sip.ListSIPOutboundTrunkResponse: + async def list_sip_inbound_trunk( + self, list: ListSIPInboundTrunkRequest + ) -> ListSIPInboundTrunkResponse: + """List SIP inbound trunks with optional filtering. + + .. deprecated:: + Use :meth:`list_inbound_trunk` instead. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP inbound trunks + """ + warnings.warn( + "list_sip_inbound_trunk is deprecated, use list_inbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.list_inbound_trunk(list) + + async def list_outbound_trunk( + self, list: ListSIPOutboundTrunkRequest + ) -> ListSIPOutboundTrunkResponse: + """List SIP outbound trunks with optional filtering. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP outbound trunks + """ return await self._client.request( SVC, "ListSIPOutboundTrunk", list, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.ListSIPOutboundTrunkResponse, + self._admin_headers(), + ListSIPOutboundTrunkResponse, ) - async def delete_sip_trunk( - self, delete: proto_sip.DeleteSIPTrunkRequest - ) -> proto_sip.SIPTrunkInfo: + async def list_sip_outbound_trunk( + self, list: ListSIPOutboundTrunkRequest + ) -> ListSIPOutboundTrunkResponse: + """List SIP outbound trunks with optional filtering. + + .. deprecated:: + Use :meth:`list_outbound_trunk` instead. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP outbound trunks + """ + warnings.warn( + "list_sip_outbound_trunk is deprecated, use list_outbound_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.list_outbound_trunk(list) + + async def delete_trunk(self, delete: DeleteSIPTrunkRequest) -> SIPTrunkInfo: + """Delete a SIP trunk. + + Args: + delete: Request containing trunk ID to delete + + Returns: + Deleted trunk information + """ return await self._client.request( SVC, "DeleteSIPTrunk", delete, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPTrunkInfo, + self._admin_headers(), + SIPTrunkInfo, ) - async def create_sip_dispatch_rule( - self, create: proto_sip.CreateSIPDispatchRuleRequest - ) -> proto_sip.SIPDispatchRuleInfo: + async def delete_sip_trunk(self, delete: DeleteSIPTrunkRequest) -> SIPTrunkInfo: + """Delete a SIP trunk. + + .. deprecated:: + Use :meth:`delete_trunk` instead. + + Args: + delete: Request containing trunk ID to delete + + Returns: + Deleted trunk information + """ + warnings.warn( + "delete_sip_trunk is deprecated, use delete_trunk instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.delete_trunk(delete) + + async def create_dispatch_rule( + self, create: CreateSIPDispatchRuleRequest + ) -> SIPDispatchRuleInfo: + """Create a new SIP dispatch rule. + + Args: + create: Request containing rule details + + Returns: + Created SIP dispatch rule + """ return await self._client.request( SVC, "CreateSIPDispatchRule", create, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPDispatchRuleInfo, + self._admin_headers(), + SIPDispatchRuleInfo, ) - async def list_sip_dispatch_rule( - self, list: proto_sip.ListSIPDispatchRuleRequest - ) -> proto_sip.ListSIPDispatchRuleResponse: + async def create_sip_dispatch_rule( + self, create: CreateSIPDispatchRuleRequest + ) -> SIPDispatchRuleInfo: + """Create a new SIP dispatch rule. + + .. deprecated:: + Use :meth:`create_dispatch_rule` instead. + + Args: + create: Request containing rule details + + Returns: + Created SIP dispatch rule + """ + warnings.warn( + "create_sip_dispatch_rule is deprecated, use create_dispatch_rule instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.create_dispatch_rule(create) + + async def update_dispatch_rule( + self, + rule_id: str, + rule: SIPDispatchRuleInfo, + ) -> SIPDispatchRuleInfo: + """Updates an existing SIP dispatch rule by replacing it entirely. + + Args: + rule_id: ID of the SIP dispatch rule to update + rule: SIP dispatch rule to update with + + Returns: + Updated SIP dispatch rule + """ + return await self._client.request( + SVC, + "UpdateSIPDispatchRule", + UpdateSIPDispatchRuleRequest(sip_dispatch_rule_id=rule_id, replace=rule), + self._admin_headers(), + SIPDispatchRuleInfo, + ) + + async def update_sip_dispatch_rule( + self, + rule_id: str, + rule: SIPDispatchRuleInfo, + ) -> SIPDispatchRuleInfo: + """Updates an existing SIP dispatch rule by replacing it entirely. + + .. deprecated:: + Use :meth:`update_dispatch_rule` instead. + + Args: + rule_id: ID of the SIP dispatch rule to update + rule: SIP dispatch rule to update with + + Returns: + Updated SIP dispatch rule + """ + warnings.warn( + "update_sip_dispatch_rule is deprecated, use update_dispatch_rule instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_dispatch_rule(rule_id, rule) + + async def update_dispatch_rule_fields( + self, + rule_id: str, + *, + trunk_ids: Optional[ListUpdate | list[str]] = None, + rule: Optional[SIPDispatchRule] = None, + name: Optional[str] = None, + metadata: Optional[str] = None, + attributes: Optional[dict[str, str]] = None, + ) -> SIPDispatchRuleInfo: + """Updates specific fields of an existing SIP dispatch rule. + + Only provided fields will be updated. + """ + update = SIPDispatchRuleUpdate( + name=name, + metadata=metadata, + rule=rule, + attributes=attributes, + ) + if trunk_ids is not None: + if isinstance(trunk_ids, ListUpdate): + update.trunk_ids.set.extend(trunk_ids.set) + update.trunk_ids.add.extend(trunk_ids.add) + update.trunk_ids.remove.extend(trunk_ids.remove) + else: + update.trunk_ids.set.extend(trunk_ids) + + return await self._client.request( + SVC, + "UpdateSIPDispatchRule", + UpdateSIPDispatchRuleRequest(sip_dispatch_rule_id=rule_id, update=update), + self._admin_headers(), + SIPDispatchRuleInfo, + ) + + async def update_sip_dispatch_rule_fields( + self, + rule_id: str, + *, + trunk_ids: Optional[list[str]] = None, + rule: Optional[SIPDispatchRule] = None, + name: Optional[str] = None, + metadata: Optional[str] = None, + attributes: Optional[dict[str, str]] = None, + ) -> SIPDispatchRuleInfo: + """Updates specific fields of an existing SIP dispatch rule. + + .. deprecated:: + Use :meth:`update_dispatch_rule_fields` instead. + + Only provided fields will be updated. + """ + warnings.warn( + "update_sip_dispatch_rule_fields is deprecated, use update_dispatch_rule_fields instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.update_dispatch_rule_fields( + rule_id, + trunk_ids=trunk_ids, + rule=rule, + name=name, + metadata=metadata, + attributes=attributes, + ) + + async def list_dispatch_rule( + self, list: ListSIPDispatchRuleRequest + ) -> ListSIPDispatchRuleResponse: + """List SIP dispatch rules with optional filtering. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP dispatch rules + """ return await self._client.request( SVC, "ListSIPDispatchRule", list, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.ListSIPDispatchRuleResponse, + self._admin_headers(), + ListSIPDispatchRuleResponse, ) - async def delete_sip_dispatch_rule( - self, delete: proto_sip.DeleteSIPDispatchRuleRequest - ) -> proto_sip.SIPDispatchRuleInfo: + async def list_sip_dispatch_rule( + self, list: ListSIPDispatchRuleRequest + ) -> ListSIPDispatchRuleResponse: + """List SIP dispatch rules with optional filtering. + + .. deprecated:: + Use :meth:`list_dispatch_rule` instead. + + Args: + list: Request with optional filtering parameters + + Returns: + Response containing list of SIP dispatch rules + """ + warnings.warn( + "list_sip_dispatch_rule is deprecated, use list_dispatch_rule instead", + DeprecationWarning, + stacklevel=2, + ) + return await self.list_dispatch_rule(list) + + async def delete_dispatch_rule( + self, delete: DeleteSIPDispatchRuleRequest + ) -> SIPDispatchRuleInfo: + """Delete a SIP dispatch rule. + + Args: + delete: Request containing rule ID to delete + + Returns: + Deleted rule information + """ return await self._client.request( SVC, "DeleteSIPDispatchRule", delete, - self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)), - proto_sip.SIPDispatchRuleInfo, + self._admin_headers(), + SIPDispatchRuleInfo, ) - async def create_sip_participant( - self, create: proto_sip.CreateSIPParticipantRequest - ) -> proto_sip.SIPParticipantInfo: - return await self._client.request( - SVC, - "CreateSIPParticipant", - create, - self._auth_header(VideoGrants(), sip=SIPGrants(call=True)), - proto_sip.SIPParticipantInfo, + async def delete_sip_dispatch_rule( + self, delete: DeleteSIPDispatchRuleRequest + ) -> SIPDispatchRuleInfo: + """Delete a SIP dispatch rule. + + .. deprecated:: + Use :meth:`delete_dispatch_rule` instead. + + Args: + delete: Request containing rule ID to delete + + Returns: + Deleted rule information + """ + warnings.warn( + "delete_sip_dispatch_rule is deprecated, use delete_dispatch_rule instead", + DeprecationWarning, + stacklevel=2, ) + return await self.delete_dispatch_rule(delete) + + async def create_sip_participant( + self, + create: CreateSIPParticipantRequest, + *, + timeout: Optional[float] = None, + trunk_id: Optional[str] = None, + outbound_trunk_config: Optional[SIPOutboundConfig] = None, + ) -> SIPParticipantInfo: + """Create a new SIP participant. + + Args: + create: Request containing participant details + timeout: Optional request timeout in seconds + trunk_id: Optional SIP trunk ID to use for the participant + outbound_trunk_config: Optional outbound trunk configuration for the participant + + Returns: + Created SIP participant + + Raises: + SIPError: If the SIP operation fails + """ + client_timeout: Optional[aiohttp.ClientTimeout] = None + if create.wait_until_answered: + # Dialing a phone and waiting for an answer takes longer than a + # normal call, and the request must outlast ringing. Pin the ring + # window so the timeout doesn't depend on the server's default. + _pin_ringing_timeout(create) + client_timeout = aiohttp.ClientTimeout(total=_dial_timeout(timeout, create)) + elif timeout: + # obey user specified timeout + client_timeout = aiohttp.ClientTimeout(total=timeout) + + if trunk_id: + create.sip_trunk_id = trunk_id + + if outbound_trunk_config: + create.trunk = outbound_trunk_config + + try: + return await self._client.request( + SVC, + "CreateSIPParticipant", + create, + self._auth_header(VideoGrants(), sip=SIPGrants(call=True)), + SIPParticipantInfo, + timeout=client_timeout, + ) + except ServerError as e: + raise _as_sip_error(e) from None + + async def transfer_sip_participant( + self, + transfer: TransferSIPParticipantRequest, + *, + timeout: Optional[float] = None, + ) -> SIPParticipantInfo: + """Transfer a SIP participant to a different room. + + Args: + transfer: Request containing transfer details + timeout: Optional request timeout in seconds. Transferring dials a + phone, which takes longer than normal, so it defaults to a + longer timeout when unset. + + Returns: + Updated SIP participant information + """ + # Transferring a call dials a phone, which takes longer than a normal + # call, so keep the request alive past ringing. Pin the ring window so the + # timeout doesn't depend on the server's default. + _pin_ringing_timeout(transfer) + client_timeout = aiohttp.ClientTimeout(total=_dial_timeout(timeout, transfer)) + try: + return await self._client.request( + SVC, + "TransferSIPParticipant", + transfer, + self._auth_header( + VideoGrants( + room_admin=True, + room=transfer.room_name, + ), + sip=SIPGrants(call=True), + ), + SIPParticipantInfo, + timeout=client_timeout, + ) + except ServerError as e: + raise _as_sip_error(e) from None + + def _admin_headers(self) -> dict[str, str]: + return self._auth_header(VideoGrants(), sip=SIPGrants(admin=True)) diff --git a/livekit-api/livekit/api/twirp_client.py b/livekit-api/livekit/api/twirp_client.py index 1c930270..a4c71e9f 100644 --- a/livekit-api/livekit/api/twirp_client.py +++ b/livekit-api/livekit/api/twirp_client.py @@ -12,19 +12,48 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Dict, Type, TypeVar +import asyncio +import logging +from typing import Dict, List, Optional, Type, TypeVar import aiohttp from google.protobuf.message import Message from urllib.parse import urlparse +from ._failover import ( + FAILOVER_BACKOFF_BASE, + RegionCache, + failover_attempts, + host_key, + origin_of, + pick_next, +) +from .version import __version__ + DEFAULT_PREFIX = "twirp" +logger = logging.getLogger("livekit") + +# Identifies the SDK and version to the server on every request. +_USER_AGENT = f"livekit-server-sdk-python/{__version__}" + +# Shared across all clients in the process so the region list is fetched once. +_REGION_CACHE = RegionCache() -class TwirpError(Exception): - def __init__(self, code: str, msg: str) -> None: + +class ServerError(Exception): + def __init__( + self, + code: str, + msg: str, + *, + status: int, + metadata: Optional[Dict[str, str]] = None, + ) -> None: self._code = code self._msg = msg + self._status = status + self._metadata = metadata or {} @property def code(self) -> str: @@ -34,8 +63,75 @@ def code(self) -> str: def message(self) -> str: return self._msg + @property + def status(self) -> int: + """HTTP status code""" + return self._status + + @property + def metadata(self) -> Dict[str, str]: + """Server-provided error metadata""" + return self._metadata + + def __str__(self) -> str: + result = f"ServerError(code={self.code}, message={self.message}, status={self.status}" + if self.metadata: + result += f", metadata={self.metadata}" + result += ")" + return result + + +class SipCallError(ServerError): + """A :class:`ServerError` from a SIP dialing call (``create_sip_participant`` / + ``transfer_sip_participant``) that failed with a SIP response status. The SIP + code and reason are exposed as properties; any other error metadata remains + available via :attr:`metadata`.""" + + @property + def sip_status_code(self) -> Optional[int]: + """The SIP response code of the failed call, e.g. 486 (Busy Here).""" + raw = self.metadata.get("sip_status_code") + if raw is None: + return None + try: + return int(raw) + except ValueError: + return None + + @property + def sip_status(self) -> Optional[str]: + """The SIP reason phrase of the failed call, e.g. "Busy Here".""" + return self.metadata.get("sip_status") -class TwirpErrorCode: + @classmethod + def from_server_error(cls, err: ServerError) -> "SipCallError": + return cls(err.code, err.message, status=err.status, metadata=err.metadata) + + def __str__(self) -> str: + code = self.metadata.get("sip_status_code") + if code is None: + return super().__str__() + # A clear, SIP-specific representation, including any extra metadata. + reason = self.metadata.get("sip_status") + result = f"SIP call failed: {code}" + if reason: + result += f" {reason}" + result += f" ({self.code})" + extra = { + k: v + for k, v in self.metadata.items() + if k not in ("sip_status_code", "sip_status", "error_details") + } + if extra: + result += " [" + ", ".join(f"{k}={v}" for k, v in extra.items()) + "]" + return result + + +# Deprecated alias for :class:`ServerError`, kept for backwards compatibility. +TwirpError = ServerError + + +class ServerErrorCode: CANCELED = "canceled" UNKNOWN = "unknown" INVALID_ARGUMENT = "invalid_argument" @@ -56,6 +152,10 @@ class TwirpErrorCode: DATA_LOSS = "dataloss" +# Deprecated alias for :class:`ServerErrorCode`, kept for backwards compatibility. +TwirpErrorCode = ServerErrorCode + + T = TypeVar("T", bound=Message) @@ -66,6 +166,10 @@ def __init__( host: str, pkg: str, prefix: str = DEFAULT_PREFIX, + failover: bool = True, + *, + _failover_force: bool = False, + _failover_backoff: float = FAILOVER_BACKOFF_BASE, ) -> None: parse_res = urlparse(host) scheme = parse_res.scheme @@ -77,6 +181,11 @@ def __init__( self.pkg = pkg self.prefix = prefix self._session = session + # _failover_force / _failover_backoff are internal test-only knobs. + self._failover = failover + self._failover_force = _failover_force + self._failover_backoff = _failover_backoff + self._origin = origin_of(self.host) async def request( self, @@ -85,17 +194,91 @@ async def request( data: Message, headers: Dict[str, str], response_class: Type[T], + *, + timeout: Optional[aiohttp.ClientTimeout] = None, ) -> T: - url = f"{self.host}/{self.prefix}/{self.pkg}.{service}/{method}" + """Issues a Twirp request, failing over to alternative regions on + retryable errors. On any transport error or HTTP 5xx it discovers + regions via ``/settings/regions`` and replays the request — body and + headers intact — against the next untried region, with exponential + backoff. A 4xx is returned immediately.""" + path = f"{self.prefix}/{self.pkg}.{service}/{method}" + headers = dict(headers) + headers["User-Agent"] = _USER_AGENT + forward_headers = dict(headers) # for the discovery fetch (no content-type yet) headers["Content-Type"] = "application/protobuf" - serialized_data = data.SerializeToString() - async with self._session.post( - url, headers=headers, data=serialized_data - ) as resp: - if resp.status == 200: - return response_class.FromString(await resp.read()) - else: - # when we have an error, Twirp always encode it in json - error_data = await resp.json() - raise TwirpError(error_data["code"], error_data["msg"]) + + # The effective per-attempt timeout is the per-call override, or the + # session default; used to gate failover for short requests. + effective_timeout = timeout.total if timeout else None + if effective_timeout is None and self._session.timeout is not None: + effective_timeout = self._session.timeout.total + host = urlparse(self._origin).hostname + max_attempts = failover_attempts( + self._failover, host, self._failover_force, effective_timeout + ) + attempted = {host_key(self._origin)} + region_origins: Optional[List[str]] = None + current_origin = self._origin + + for attempt in range(max_attempts): + is_last = attempt + 1 >= max_attempts + url = f"{current_origin}/{path}" + + transport_exc: Optional[BaseException] = None + retryable_status: Optional[int] = None + error_data: Dict = {} + try: + async with self._session.post( + url, headers=headers, data=serialized_data, timeout=timeout + ) as resp: + if resp.status == 200: + return response_class.FromString(await resp.read()) + # Twirp encodes errors as JSON regardless of content type. + try: + error_data = await resp.json() + except Exception: + error_data = {} + if resp.status < 500: + # 4xx is terminal. + raise self._server_error(error_data, resp.status) + retryable_status = resp.status + except (aiohttp.ClientError, asyncio.TimeoutError) as e: + transport_exc = e + + # Only retryable failures (5xx or transport error) reach this point. + next_origin = None + if not is_last: + if region_origins is None: + region_origins = await _REGION_CACHE.region_origins( + self._session, self._origin, forward_headers + ) + next_origin = pick_next(region_origins, attempted) + + if next_origin is None: + if transport_exc is not None: + raise transport_exc + raise self._server_error(error_data, retryable_status or 500) + + reason = transport_exc if transport_exc is not None else f"status {retryable_status}" + logger.warning( + "livekit API request to %s failed (%s), retrying with fallback url %s", + current_origin, + reason, + next_origin, + ) + await asyncio.sleep(self._failover_backoff * (2**attempt)) + attempted.add(host_key(next_origin)) + current_origin = next_origin + + raise RuntimeError("failover loop exited without returning") # unreachable + + @staticmethod + def _server_error(error_data: Dict, status: int) -> "ServerError": + return ServerError( + error_data.get("code", "unknown"), + error_data.get("msg", ""), + status=status, + metadata=error_data.get("meta"), + ) diff --git a/livekit-api/livekit/api/version.py b/livekit-api/livekit/api/version.py index dd9b22cc..c68196d1 100644 --- a/livekit-api/livekit/api/version.py +++ b/livekit-api/livekit/api/version.py @@ -1 +1 @@ -__version__ = "0.5.1" +__version__ = "1.2.0" diff --git a/livekit-api/livekit/api/webhook.py b/livekit-api/livekit/api/webhook.py index 760c9a90..9f500637 100644 --- a/livekit-api/livekit/api/webhook.py +++ b/livekit-api/livekit/api/webhook.py @@ -1,5 +1,5 @@ from .access_token import TokenVerifier -from livekit.protocol import webhook as proto_webhook +from livekit.protocol.webhook import WebhookEvent from google.protobuf.json_format import Parse import hashlib import base64 @@ -9,8 +9,10 @@ class WebhookReceiver: def __init__(self, token_verifier: TokenVerifier): self._verifier = token_verifier - def receive(self, body: str, auth_token: str) -> proto_webhook.WebhookEvent: + def receive(self, body: str, auth_token: str) -> WebhookEvent: claims = self._verifier.verify(auth_token) + if claims.sha256 is None: + raise Exception("sha256 was not found in the token") body_hash = hashlib.sha256(body.encode()).digest() claims_hash = base64.b64decode(claims.sha256) @@ -18,4 +20,4 @@ def receive(self, body: str, auth_token: str) -> proto_webhook.WebhookEvent: if body_hash != claims_hash: raise Exception("hash mismatch") - return Parse(body, proto_webhook.WebhookEvent(), ignore_unknown_fields=True) + return Parse(body, WebhookEvent(), ignore_unknown_fields=True) diff --git a/livekit-api/pyproject.toml b/livekit-api/pyproject.toml index 15caa0ee..393f7c80 100644 --- a/livekit-api/pyproject.toml +++ b/livekit-api/pyproject.toml @@ -1,5 +1,53 @@ [build-system] -requires = [ - "setuptools>=42", +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "livekit-api" +dynamic = ["version"] +description = "Python Server API for LiveKit" +readme = "README.md" +requires-python = ">=3.9.0" +license = "Apache-2.0" +keywords = ["webrtc", "realtime", "audio", "video", "livekit"] +authors = [ + { name = "LiveKit", email = "support@livekit.io" } ] -build-backend = "setuptools.build_meta" +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Multimedia :: Sound/Audio", + "Topic :: Multimedia :: Video", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3 :: Only", +] +dependencies = [ + "pyjwt>=2.0.0", + "aiohttp>=3.9.0", + "protobuf>=4", + "types-protobuf>=4", + "livekit-protocol>=1.1.22,<2.0.0", +] + +[project.urls] +Documentation = "https://docs.livekit.io" +Website = "https://livekit.io/" +Source = "https://github.com/livekit/python-sdks/" + +[tool.uv.sources] +livekit-protocol = { workspace = true } + +[tool.hatch.version] +path = "livekit/api/version.py" + +[tool.hatch.build.targets.wheel] +packages = ["livekit"] + +[tool.hatch.build.targets.sdist] +include = ["/livekit"] diff --git a/livekit-api/setup.py b/livekit-api/setup.py deleted file mode 100644 index ae57e4d0..00000000 --- a/livekit-api/setup.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright 2023 LiveKit, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import pathlib -from typing import Any, Dict - -import setuptools # type: ignore - -here = pathlib.Path(__file__).parent.resolve() -about: Dict[Any, Any] = {} -with open(os.path.join(here, "livekit", "api", "version.py"), "r") as f: - exec(f.read(), about) - - -setuptools.setup( - name="livekit-api", - version=about["__version__"], - description="Python Server API for LiveKit", - long_description=(here / "README.md").read_text(encoding="utf-8"), - long_description_content_type="text/markdown", - url="https://github.com/livekit/python-sdks", - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Topic :: Multimedia :: Sound/Audio", - "Topic :: Multimedia :: Video", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3 :: Only", - ], - keywords=["webrtc", "realtime", "audio", "video", "livekit"], - license="Apache-2.0", - packages=setuptools.find_namespace_packages(include=["livekit.*"]), - python_requires=">=3.9.0", - install_requires=[ - "pyjwt>=2.0.0", - "aiohttp>=3.9.0", - "protobuf>=3", - "types-protobuf>=4,<5", - "livekit-protocol>=0.5.1,<2", - ], - package_data={ - "livekit.api": ["py.typed", "*.pyi", "**/*.pyi"], - }, - project_urls={ - "Documentation": "https://docs.livekit.io", - "Website": "https://livekit.io/", - "Source": "https://github.com/livekit/python-sdks/", - }, -) diff --git a/livekit-api/tests/test_access_token.py b/livekit-api/tests/test_access_token.py deleted file mode 100644 index 76c986a6..00000000 --- a/livekit-api/tests/test_access_token.py +++ /dev/null @@ -1,60 +0,0 @@ -import datetime - -import pytest # type: ignore -from livekit.api import AccessToken, TokenVerifier, VideoGrants, SIPGrants - -TEST_API_KEY = "myapikey" -TEST_API_SECRET = "thiskeyistotallyunsafe" - - -def test_verify_token(): - grants = VideoGrants(room_join=True, room="test_room") - sip = SIPGrants(admin=True) - - token = ( - AccessToken(TEST_API_KEY, TEST_API_SECRET) - .with_identity("test_identity") - .with_metadata("test_metadata") - .with_grants(grants) - .with_sip_grants(sip) - .to_jwt() - ) - - token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) - claims = token_verifier.verify(token) - - assert claims.identity == "test_identity" - assert claims.metadata == "test_metadata" - assert claims.video == grants - assert claims.sip == sip - - -def test_verify_token_invalid(): - token = ( - AccessToken(TEST_API_KEY, TEST_API_SECRET) - .with_identity("test_identity") - .to_jwt() - ) - - token_verifier = TokenVerifier(TEST_API_KEY, "invalid_secret") - with pytest.raises(Exception): - token_verifier.verify(token) - - token_verifier = TokenVerifier("invalid_key", TEST_API_SECRET) - with pytest.raises(Exception): - token_verifier.verify(token) - - -def test_verify_token_expired(): - token = ( - AccessToken(TEST_API_KEY, TEST_API_SECRET) - .with_identity("test_identity") - .with_ttl(datetime.timedelta(seconds=-1)) - .to_jwt() - ) - - token_verifier = TokenVerifier( - TEST_API_KEY, TEST_API_SECRET, leeway=datetime.timedelta(seconds=0) - ) - with pytest.raises(Exception): - token_verifier.verify(token) diff --git a/livekit-protocol/generate_proto.sh b/livekit-protocol/generate_proto.sh index 692ec7a5..8129775e 100755 --- a/livekit-protocol/generate_proto.sh +++ b/livekit-protocol/generate_proto.sh @@ -18,6 +18,49 @@ set -e + +# --- protoc / gencode version guard ------------------------------------------- +# +# Generated *_pb2.py stubs (protobuf>=5) embed a "gencode" version and a runtime +# check that REFUSES to import when the installed protobuf runtime is OLDER than +# the gencode (raising google.protobuf.runtime_version.VersionError). The only +# rule is runtime >= gencode; there is NO lower bound, so gencode-N stubs load on +# any runtime >= N (N, N+1, ...). +# +# IMPORTANT LIMITATION: the protobuf runtime is capped at <7 (tops out at 6.x) +# because livekit-agents pulls deps that pin protobuf<7 — opentelemetry-proto +# (via opentelemetry-exporter-otlp) and grpcio-tools (via the nvidia plugin). +# +# We target gencode 5 (protoc 26.x-29.x, e.g. protoc 29.3 => gencode 5.29.x): +# * gencode 5 needs only runtime >= 5, so it works on every 5.x / 6.x runtime +# AND a future 7.x — no regen needed when the <7 cap is eventually lifted. +# * gencode 6 would be minor-sensitive against today's pinned 6.x runtime +# (e.g. gencode 6.34 fails to import on runtime 6.33), so it is NOT safe yet. +# * gencode 7+ (libprotoc 35+) requires runtime >= 7 and breaks outright today. +# This guard rejects gencode > 5. Ships stubs require protobuf>=5 (see pyproject). +MAX_GENCODE_MAJOR=5 + +_probe_dir=$(mktemp -d) +trap 'rm -rf "$_probe_dir"' EXIT +printf 'syntax = "proto3";\nmessage _ProtocVersionProbe {}\n' > "$_probe_dir/probe.proto" +protoc -I="$_probe_dir" --python_out="$_probe_dir" "$_probe_dir/probe.proto" +gencode_major=$(sed -n 's/^# Protobuf Python Version: \([0-9][0-9]*\).*/\1/p' "$_probe_dir/probe_pb2.py") + +if [ -z "$gencode_major" ]; then + # protoc <3.20 didn't stamp a version line; that predates the runtime guard + # entirely, so it's safe. + echo "note: protoc ($(protoc --version)) emits no gencode version stamp (pre-guard); proceeding." +elif [ "$gencode_major" -gt "$MAX_GENCODE_MAJOR" ]; then + echo "ERROR: protoc ($(protoc --version)) emits gencode major ${gencode_major}, but this" >&2 + echo " package targets gencode ${MAX_GENCODE_MAJOR} (see the comment above). The" >&2 + echo " protobuf runtime is capped at <7 (6.x) by livekit-agents dependencies" >&2 + echo " (opentelemetry-proto, grpcio-tools), and gencode 6+ is not safe against it." >&2 + echo " Install protoc 26.x-29.x (e.g. a pinned protoc-29.3 download, or" >&2 + echo " 'brew install protobuf@29'); protoc 29.3 emits gencode 5.29.x." >&2 + exit 1 +fi + + API_PROTOCOL=./protocol/protobufs API_OUT_PYTHON=./livekit/protocol @@ -31,8 +74,22 @@ protoc \ $API_PROTOCOL/livekit_ingress.proto \ $API_PROTOCOL/livekit_models.proto \ $API_PROTOCOL/livekit_agent.proto \ + $API_PROTOCOL/livekit_agent_dispatch.proto \ + $API_PROTOCOL/livekit_agent_worker.proto \ + $API_PROTOCOL/livekit_metrics.proto \ $API_PROTOCOL/livekit_sip.proto \ - $API_PROTOCOL/livekit_analytics.proto + $API_PROTOCOL/livekit_analytics.proto \ + $API_PROTOCOL/livekit_rtc.proto \ + $API_PROTOCOL/livekit_cloud_agent.proto \ + $API_PROTOCOL/livekit_agent_simulation.proto \ + $API_PROTOCOL/agent/livekit_agent_session.proto \ + $API_PROTOCOL/agent/livekit_agent_inference.proto \ + $API_PROTOCOL/agent/livekit_agent_text.proto \ + $API_PROTOCOL/agent/livekit_agent_dev.proto \ + $API_PROTOCOL/logger/options.proto \ + $API_PROTOCOL/livekit_connector_whatsapp.proto \ + $API_PROTOCOL/livekit_connector_twilio.proto \ + $API_PROTOCOL/livekit_connector.proto touch -a "$API_OUT_PYTHON/__init__.py" @@ -58,11 +115,58 @@ mv "$API_OUT_PYTHON/livekit_models_pb2.py" "$API_OUT_PYTHON/models.py" mv "$API_OUT_PYTHON/livekit_models_pb2.pyi" "$API_OUT_PYTHON/models.pyi" mv "$API_OUT_PYTHON/livekit_agent_pb2.py" "$API_OUT_PYTHON/agent.py" mv "$API_OUT_PYTHON/livekit_agent_pb2.pyi" "$API_OUT_PYTHON/agent.pyi" +mv "$API_OUT_PYTHON/livekit_agent_dispatch_pb2.py" "$API_OUT_PYTHON/agent_dispatch.py" +mv "$API_OUT_PYTHON/livekit_agent_dispatch_pb2.pyi" "$API_OUT_PYTHON/agent_dispatch.pyi" +mv "$API_OUT_PYTHON/livekit_agent_worker_pb2.py" "$API_OUT_PYTHON/agent_worker.py" +mv "$API_OUT_PYTHON/livekit_agent_worker_pb2.pyi" "$API_OUT_PYTHON/agent_worker.pyi" mv "$API_OUT_PYTHON/livekit_analytics_pb2.py" "$API_OUT_PYTHON/analytics.py" mv "$API_OUT_PYTHON/livekit_analytics_pb2.pyi" "$API_OUT_PYTHON/analytics.pyi" +mv "$API_OUT_PYTHON/livekit_cloud_agent_pb2.py" "$API_OUT_PYTHON/cloud_agent.py" +mv "$API_OUT_PYTHON/livekit_cloud_agent_pb2.pyi" "$API_OUT_PYTHON/cloud_agent.pyi" +mv "$API_OUT_PYTHON/livekit_agent_simulation_pb2.py" "$API_OUT_PYTHON/agent_simulation.py" +mv "$API_OUT_PYTHON/livekit_agent_simulation_pb2.pyi" "$API_OUT_PYTHON/agent_simulation.pyi" mv "$API_OUT_PYTHON/livekit_sip_pb2.py" "$API_OUT_PYTHON/sip.py" mv "$API_OUT_PYTHON/livekit_sip_pb2.pyi" "$API_OUT_PYTHON/sip.pyi" +mv "$API_OUT_PYTHON/livekit_metrics_pb2.py" "$API_OUT_PYTHON/metrics.py" +mv "$API_OUT_PYTHON/livekit_metrics_pb2.pyi" "$API_OUT_PYTHON/metrics.pyi" +mv "$API_OUT_PYTHON/livekit_rtc_pb2.py" "$API_OUT_PYTHON/rtc.py" +mv "$API_OUT_PYTHON/livekit_rtc_pb2.pyi" "$API_OUT_PYTHON/rtc.pyi" +mv "$API_OUT_PYTHON/livekit_connector_whatsapp_pb2.py" "$API_OUT_PYTHON/connector_whatsapp.py" +mv "$API_OUT_PYTHON/livekit_connector_whatsapp_pb2.pyi" "$API_OUT_PYTHON/connector_whatsapp.pyi" +mv "$API_OUT_PYTHON/livekit_connector_twilio_pb2.py" "$API_OUT_PYTHON/connector_twilio.py" +mv "$API_OUT_PYTHON/livekit_connector_twilio_pb2.pyi" "$API_OUT_PYTHON/connector_twilio.pyi" +mv "$API_OUT_PYTHON/livekit_connector_pb2.py" "$API_OUT_PYTHON/connector.py" +mv "$API_OUT_PYTHON/livekit_connector_pb2.pyi" "$API_OUT_PYTHON/connector.pyi" + +mkdir -p "$API_OUT_PYTHON/agent_pb" +mv "$API_OUT_PYTHON/agent/livekit_agent_inference_pb2.py" "$API_OUT_PYTHON/agent_pb/agent_inference.py" +mv "$API_OUT_PYTHON/agent/livekit_agent_inference_pb2.pyi" "$API_OUT_PYTHON/agent_pb/agent_inference.pyi" +mv "$API_OUT_PYTHON/agent/livekit_agent_session_pb2.py" "$API_OUT_PYTHON/agent_pb/agent_session.py" +mv "$API_OUT_PYTHON/agent/livekit_agent_session_pb2.pyi" "$API_OUT_PYTHON/agent_pb/agent_session.pyi" +mv "$API_OUT_PYTHON/agent/livekit_agent_text_pb2.py" "$API_OUT_PYTHON/agent_pb/agent_text.py" +mv "$API_OUT_PYTHON/agent/livekit_agent_text_pb2.pyi" "$API_OUT_PYTHON/agent_pb/agent_text.pyi" +mv "$API_OUT_PYTHON/agent/livekit_agent_dev_pb2.py" "$API_OUT_PYTHON/agent_pb/agent_dev.py" +mv "$API_OUT_PYTHON/agent/livekit_agent_dev_pb2.pyi" "$API_OUT_PYTHON/agent_pb/agent_dev.pyi" + +mkdir -p "$API_OUT_PYTHON/logger_pb" +mv "$API_OUT_PYTHON/logger/options_pb2.py" "$API_OUT_PYTHON/logger_pb/options.py" +mv "$API_OUT_PYTHON/logger/options_pb2.pyi" "$API_OUT_PYTHON/logger_pb/options.pyi" + +find "$API_OUT_PYTHON" -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|^(import (livekit_egress_pb2\|livekit_room_pb2\|livekit_webhook_pb2\|livekit_ingress_pb2\|livekit_models_pb2\|livekit_agent_pb2\|livekit_agent_dispatch_pb2\|livekit_agent_worker_pb2\|livekit_analytics_pb2\|livekit_sip_pb2\|livekit_metrics_pb2\|livekit_rtc_pb2\|livekit_cloud_agent_pb2\|livekit_agent_simulation_pb2\|livekit_connector_whatsapp_pb2\|livekit_connector_twilio_pb2\|livekit_connector_pb2\|livekit_agent_session_pb2\|livekit_agent_inference_pb2\|livekit_agent_dev_pb2\|livekit_agent_text_pb2\|options_pb2))|from . $1|g' + +find "$API_OUT_PYTHON" -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|livekit_(\w+)_pb2|${1}|g' + +# fix logger imports for top-level files +find "$API_OUT_PYTHON" -maxdepth 1 -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|from logger import options_pb2 as ([^ ]+)|from .logger_pb import options as $1|g' + +# fix logger imports for files in subdirectories (need parent-relative import) +find "$API_OUT_PYTHON" -mindepth 2 -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|from logger import options_pb2 as ([^ ]+)|from ..logger_pb import options as $1|g' + +# fix `from agent import agent_xxx as xxx` to `from . import agent_xxx as xxx` +find "$API_OUT_PYTHON"/agent_pb -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|from agent import (agent_\w+) as ([^ ]+)|from . import $1 as $2|g' -perl -i -pe 's|^(import (livekit_egress_pb2\|livekit_room_pb2\|livekit_webhook_pb2\|livekit_ingress_pb2\|livekit_models_pb2\|livekit_agent_pb2\|livekit_analytics_pb2\|livekit_sip_pb2))|from . $1|g' "$API_OUT_PYTHON"/*.py "$API_OUT_PYTHON"/*.pyi +# top-level files (e.g. agent_simulation) importing from agent/ -> agent_pb subpackage +find "$API_OUT_PYTHON" -maxdepth 1 -name '*.py' -o -name '*.pyi' | xargs perl -i -pe 's|from agent import (agent_\w+) as ([^ ]+)|from .agent_pb import $1 as $2|g' -perl -i -pe 's|livekit_(\w+)_pb2|${1}|g' "$API_OUT_PYTHON"/*.py "$API_OUT_PYTHON"/*.pyi +# fixes - error: ClassVar can only be used for assignments in class body [misc] +perl -i -pe 's|^(\w+_FIELD_NUMBER): _ClassVar\[int\]|$1: int|g' "$API_OUT_PYTHON/logger_pb/options.pyi" diff --git a/livekit-protocol/livekit/protocol/__init__.py b/livekit-protocol/livekit/protocol/__init__.py index b6e0b419..42dd3ef1 100644 --- a/livekit-protocol/livekit/protocol/__init__.py +++ b/livekit-protocol/livekit/protocol/__init__.py @@ -1,9 +1,34 @@ from . import agent +from . import agent_pb +from . import agent_dispatch +from . import agent_simulation +from . import agent_worker from . import analytics +from . import cloud_agent from . import egress from . import ingress +from . import metrics from . import models from . import room from . import webhook from . import sip from .version import __version__ + + +__all__ = [ + "agent", + "agent_pb", + "agent_dispatch", + "agent_simulation", + "agent_worker", + "analytics", + "cloud_agent", + "egress", + "ingress", + "metrics", + "models", + "room", + "webhook", + "sip", + "__version__", +] diff --git a/livekit-protocol/livekit/protocol/agent.py b/livekit-protocol/livekit/protocol/agent.py index b83ff034..5116b2c6 100644 --- a/livekit-protocol/livekit/protocol/agent.py +++ b/livekit-protocol/livekit/protocol/agent.py @@ -1,64 +1,103 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_agent.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_agent.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from . import models as _models_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13livekit_agent.proto\x12\x07livekit\x1a\x14livekit_models.proto\"\xa7\x01\n\nWorkerInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tnamespace\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x1e\n\x04type\x18\x05 \x01(\x0e\x32\x10.livekit.JobType\x12;\n\x13\x61llowed_permissions\x18\x06 \x01(\x0b\x32\x1e.livekit.ParticipantPermission\"6\n\tAgentInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"\xa5\x01\n\x03Job\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1e\n\x04type\x18\x02 \x01(\x0e\x32\x10.livekit.JobType\x12\x1b\n\x04room\x18\x03 \x01(\x0b\x32\r.livekit.Room\x12\x32\n\x0bparticipant\x18\x04 \x01(\x0b\x32\x18.livekit.ParticipantInfoH\x00\x88\x01\x01\x12\x11\n\tnamespace\x18\x05 \x01(\tB\x0e\n\x0c_participant\"\xf8\x02\n\rWorkerMessage\x12\x32\n\x08register\x18\x01 \x01(\x0b\x32\x1e.livekit.RegisterWorkerRequestH\x00\x12\x35\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x1d.livekit.AvailabilityResponseH\x00\x12\x34\n\rupdate_worker\x18\x03 \x01(\x0b\x32\x1b.livekit.UpdateWorkerStatusH\x00\x12.\n\nupdate_job\x18\x04 \x01(\x0b\x32\x18.livekit.UpdateJobStatusH\x00\x12#\n\x04ping\x18\x05 \x01(\x0b\x32\x13.livekit.WorkerPingH\x00\x12\x33\n\x0csimulate_job\x18\x06 \x01(\x0b\x32\x1b.livekit.SimulateJobRequestH\x00\x12\x31\n\x0bmigrate_job\x18\x07 \x01(\x0b\x32\x1a.livekit.MigrateJobRequestH\x00\x42\t\n\x07message\"\xd8\x01\n\rServerMessage\x12\x33\n\x08register\x18\x01 \x01(\x0b\x32\x1f.livekit.RegisterWorkerResponseH\x00\x12\x34\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x1c.livekit.AvailabilityRequestH\x00\x12,\n\nassignment\x18\x03 \x01(\x0b\x32\x16.livekit.JobAssignmentH\x00\x12#\n\x04pong\x18\x04 \x01(\x0b\x32\x13.livekit.WorkerPongH\x00\x42\t\n\x07message\"\x80\x01\n\x12SimulateJobRequest\x12\x1e\n\x04type\x18\x01 \x01(\x0e\x32\x10.livekit.JobType\x12\x1b\n\x04room\x18\x02 \x01(\x0b\x32\r.livekit.Room\x12-\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x18.livekit.ParticipantInfo\"\x1f\n\nWorkerPing\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\"7\n\nWorkerPong\x12\x16\n\x0elast_timestamp\x18\x01 \x01(\x03\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\"\xd0\x01\n\x15RegisterWorkerRequest\x12\x1e\n\x04type\x18\x01 \x01(\x0e\x32\x10.livekit.JobType\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x15\n\rping_interval\x18\x05 \x01(\r\x12\x16\n\tnamespace\x18\x06 \x01(\tH\x00\x88\x01\x01\x12;\n\x13\x61llowed_permissions\x18\x07 \x01(\x0b\x32\x1e.livekit.ParticipantPermissionB\x0c\n\n_namespace\"U\n\x16RegisterWorkerResponse\x12\x11\n\tworker_id\x18\x01 \x01(\t\x12(\n\x0bserver_info\x18\x03 \x01(\x0b\x32\x13.livekit.ServerInfo\"#\n\x11MigrateJobRequest\x12\x0e\n\x06job_id\x18\x01 \x01(\t\"B\n\x13\x41vailabilityRequest\x12\x19\n\x03job\x18\x01 \x01(\x0b\x32\x0c.livekit.Job\x12\x10\n\x08resuming\x18\x02 \x01(\x08\"\xa8\x01\n\x14\x41vailabilityResponse\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\x11\n\tavailable\x18\x02 \x01(\x08\x12\x17\n\x0fsupports_resume\x18\x03 \x01(\x08\x12\x18\n\x10participant_name\x18\x04 \x01(\t\x12\x1c\n\x14participant_identity\x18\x05 \x01(\t\x12\x1c\n\x14participant_metadata\x18\x06 \x01(\t\"\x96\x01\n\x0fUpdateJobStatus\x12\x0e\n\x06job_id\x18\x01 \x01(\t\x12\'\n\x06status\x18\x02 \x01(\x0e\x32\x12.livekit.JobStatusH\x00\x88\x01\x01\x12\r\n\x05\x65rror\x18\x03 \x01(\t\x12\x15\n\x08metadata\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x0c\n\x04load\x18\x05 \x01(\x02\x42\t\n\x07_statusB\x0b\n\t_metadata\"}\n\x12UpdateWorkerStatus\x12*\n\x06status\x18\x01 \x01(\x0e\x32\x15.livekit.WorkerStatusH\x00\x88\x01\x01\x12\x15\n\x08metadata\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x0c\n\x04load\x18\x03 \x01(\x02\x42\t\n\x07_statusB\x0b\n\t_metadata\"S\n\rJobAssignment\x12\x19\n\x03job\x18\x01 \x01(\x0b\x32\x0c.livekit.Job\x12\x10\n\x03url\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05token\x18\x03 \x01(\tB\x06\n\x04_url*(\n\x07JobType\x12\x0b\n\x07JT_ROOM\x10\x00\x12\x10\n\x0cJT_PUBLISHER\x10\x01*-\n\x0cWorkerStatus\x12\x10\n\x0cWS_AVAILABLE\x10\x00\x12\x0b\n\x07WS_FULL\x10\x01*:\n\tJobStatus\x12\x0e\n\nJS_UNKNOWN\x10\x00\x12\x0e\n\nJS_SUCCESS\x10\x01\x12\r\n\tJS_FAILED\x10\x02\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13livekit_agent.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14logger/options.proto\"\xcc\x03\n\x03Job\x12\n\n\x02id\x18\x01 \x01(\t\x12\"\n\x0b\x64ispatch_id\x18\t \x01(\tB\r\xbaP\ndispatchID\x12\x1e\n\x04type\x18\x02 \x01(\x0e\x32\x10.livekit.JobType\x12\x1b\n\x04room\x18\x03 \x01(\x0b\x32\r.livekit.Room\x12\x32\n\x0bparticipant\x18\x04 \x01(\x0b\x32\x18.livekit.ParticipantInfoH\x00\x88\x01\x01\x12\x15\n\tnamespace\x18\x05 \x01(\tB\x02\x18\x01\x12\x15\n\x08metadata\x18\x06 \x01(\tB\x03\xc0P\x01\x12\x12\n\nagent_name\x18\x07 \x01(\t\x12 \n\x05state\x18\x08 \x01(\x0b\x32\x11.livekit.JobState\x12\x18\n\x10\x65nable_recording\x18\n \x01(\x08\x12\x12\n\ndeployment\x18\x0b \x01(\t\x12\x35\n\nattributes\x18\x0c \x03(\x0b\x32\x1c.livekit.Job.AttributesEntryB\x03\xc0P\x01\x12\x18\n\x10\x65nable_redaction\x18\r \x01(\x08\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0e\n\x0c_participant\"\xd3\x01\n\x08JobState\x12\"\n\x06status\x18\x01 \x01(\x0e\x32\x12.livekit.JobStatus\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12\x12\n\nstarted_at\x18\x03 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x04 \x01(\x03\x12\x12\n\nupdated_at\x18\x05 \x01(\x03\x12\x1c\n\x14participant_identity\x18\x06 \x01(\t\x12\x1e\n\tworker_id\x18\x07 \x01(\tB\x0b\xbaP\x08workerID\x12\x1c\n\x08\x61gent_id\x18\x08 \x01(\tB\n\xbaP\x07\x61gentID\"\xf8\x02\n\rWorkerMessage\x12\x32\n\x08register\x18\x01 \x01(\x0b\x32\x1e.livekit.RegisterWorkerRequestH\x00\x12\x35\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x1d.livekit.AvailabilityResponseH\x00\x12\x34\n\rupdate_worker\x18\x03 \x01(\x0b\x32\x1b.livekit.UpdateWorkerStatusH\x00\x12.\n\nupdate_job\x18\x04 \x01(\x0b\x32\x18.livekit.UpdateJobStatusH\x00\x12#\n\x04ping\x18\x05 \x01(\x0b\x32\x13.livekit.WorkerPingH\x00\x12\x33\n\x0csimulate_job\x18\x06 \x01(\x0b\x32\x1b.livekit.SimulateJobRequestH\x00\x12\x31\n\x0bmigrate_job\x18\x07 \x01(\x0b\x32\x1a.livekit.MigrateJobRequestH\x00\x42\t\n\x07message\"\x88\x02\n\rServerMessage\x12\x33\n\x08register\x18\x01 \x01(\x0b\x32\x1f.livekit.RegisterWorkerResponseH\x00\x12\x34\n\x0c\x61vailability\x18\x02 \x01(\x0b\x32\x1c.livekit.AvailabilityRequestH\x00\x12,\n\nassignment\x18\x03 \x01(\x0b\x32\x16.livekit.JobAssignmentH\x00\x12.\n\x0btermination\x18\x05 \x01(\x0b\x32\x17.livekit.JobTerminationH\x00\x12#\n\x04pong\x18\x04 \x01(\x0b\x32\x13.livekit.WorkerPongH\x00\x42\t\n\x07message\"\x80\x01\n\x12SimulateJobRequest\x12\x1e\n\x04type\x18\x01 \x01(\x0e\x32\x10.livekit.JobType\x12\x1b\n\x04room\x18\x02 \x01(\x0b\x32\r.livekit.Room\x12-\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x18.livekit.ParticipantInfo\"\x1f\n\nWorkerPing\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\"7\n\nWorkerPong\x12\x16\n\x0elast_timestamp\x18\x01 \x01(\x03\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\"\xea\x01\n\x15RegisterWorkerRequest\x12\x1e\n\x04type\x18\x01 \x01(\x0e\x32\x10.livekit.JobType\x12\x12\n\nagent_name\x18\x08 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x15\n\rping_interval\x18\x05 \x01(\r\x12\x16\n\tnamespace\x18\x06 \x01(\tH\x00\x88\x01\x01\x12;\n\x13\x61llowed_permissions\x18\x07 \x01(\x0b\x32\x1e.livekit.ParticipantPermission\x12\x12\n\ndeployment\x18\t \x01(\tB\x0c\n\n_namespace\"b\n\x16RegisterWorkerResponse\x12\x1e\n\tworker_id\x18\x01 \x01(\tB\x0b\xbaP\x08workerID\x12(\n\x0bserver_info\x18\x03 \x01(\x0b\x32\x13.livekit.ServerInfo\"$\n\x11MigrateJobRequest\x12\x0f\n\x07job_ids\x18\x02 \x03(\t\"B\n\x13\x41vailabilityRequest\x12\x19\n\x03job\x18\x01 \x01(\x0b\x32\x0c.livekit.Job\x12\x10\n\x08resuming\x18\x02 \x01(\x08\"\xdd\x02\n\x14\x41vailabilityResponse\x12\x18\n\x06job_id\x18\x01 \x01(\tB\x08\xbaP\x05jobID\x12\x11\n\tavailable\x18\x02 \x01(\x08\x12\x17\n\x0fsupports_resume\x18\x03 \x01(\x08\x12\x11\n\tterminate\x18\x08 \x01(\x08\x12\x18\n\x10participant_name\x18\x04 \x01(\t\x12\x1c\n\x14participant_identity\x18\x05 \x01(\t\x12\x1c\n\x14participant_metadata\x18\x06 \x01(\t\x12X\n\x16participant_attributes\x18\x07 \x03(\x0b\x32\x38.livekit.AvailabilityResponse.ParticipantAttributesEntry\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"^\n\x0fUpdateJobStatus\x12\x18\n\x06job_id\x18\x01 \x01(\tB\x08\xbaP\x05jobID\x12\"\n\x06status\x18\x02 \x01(\x0e\x32\x12.livekit.JobStatus\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"l\n\x12UpdateWorkerStatus\x12*\n\x06status\x18\x01 \x01(\x0e\x32\x15.livekit.WorkerStatusH\x00\x88\x01\x01\x12\x0c\n\x04load\x18\x03 \x01(\x02\x12\x11\n\tjob_count\x18\x04 \x01(\rB\t\n\x07_status\"S\n\rJobAssignment\x12\x19\n\x03job\x18\x01 \x01(\x0b\x32\x0c.livekit.Job\x12\x10\n\x03url\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05token\x18\x03 \x01(\tB\x06\n\x04_url\"*\n\x0eJobTermination\x12\x18\n\x06job_id\x18\x01 \x01(\tB\x08\xbaP\x05jobID*<\n\x07JobType\x12\x0b\n\x07JT_ROOM\x10\x00\x12\x10\n\x0cJT_PUBLISHER\x10\x01\x12\x12\n\x0eJT_PARTICIPANT\x10\x02*-\n\x0cWorkerStatus\x12\x10\n\x0cWS_AVAILABLE\x10\x00\x12\x0b\n\x07WS_FULL\x10\x01*J\n\tJobStatus\x12\x0e\n\nJS_PENDING\x10\x00\x12\x0e\n\nJS_RUNNING\x10\x01\x12\x0e\n\nJS_SUCCESS\x10\x02\x12\r\n\tJS_FAILED\x10\x03\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_JOBTYPE']._serialized_start=2206 - _globals['_JOBTYPE']._serialized_end=2246 - _globals['_WORKERSTATUS']._serialized_start=2248 - _globals['_WORKERSTATUS']._serialized_end=2293 - _globals['_JOBSTATUS']._serialized_start=2295 - _globals['_JOBSTATUS']._serialized_end=2353 - _globals['_WORKERINFO']._serialized_start=55 - _globals['_WORKERINFO']._serialized_end=222 - _globals['_AGENTINFO']._serialized_start=224 - _globals['_AGENTINFO']._serialized_end=278 - _globals['_JOB']._serialized_start=281 - _globals['_JOB']._serialized_end=446 - _globals['_WORKERMESSAGE']._serialized_start=449 - _globals['_WORKERMESSAGE']._serialized_end=825 - _globals['_SERVERMESSAGE']._serialized_start=828 - _globals['_SERVERMESSAGE']._serialized_end=1044 - _globals['_SIMULATEJOBREQUEST']._serialized_start=1047 - _globals['_SIMULATEJOBREQUEST']._serialized_end=1175 - _globals['_WORKERPING']._serialized_start=1177 - _globals['_WORKERPING']._serialized_end=1208 - _globals['_WORKERPONG']._serialized_start=1210 - _globals['_WORKERPONG']._serialized_end=1265 - _globals['_REGISTERWORKERREQUEST']._serialized_start=1268 - _globals['_REGISTERWORKERREQUEST']._serialized_end=1476 - _globals['_REGISTERWORKERRESPONSE']._serialized_start=1478 - _globals['_REGISTERWORKERRESPONSE']._serialized_end=1563 - _globals['_MIGRATEJOBREQUEST']._serialized_start=1565 - _globals['_MIGRATEJOBREQUEST']._serialized_end=1600 - _globals['_AVAILABILITYREQUEST']._serialized_start=1602 - _globals['_AVAILABILITYREQUEST']._serialized_end=1668 - _globals['_AVAILABILITYRESPONSE']._serialized_start=1671 - _globals['_AVAILABILITYRESPONSE']._serialized_end=1839 - _globals['_UPDATEJOBSTATUS']._serialized_start=1842 - _globals['_UPDATEJOBSTATUS']._serialized_end=1992 - _globals['_UPDATEWORKERSTATUS']._serialized_start=1994 - _globals['_UPDATEWORKERSTATUS']._serialized_end=2119 - _globals['_JOBASSIGNMENT']._serialized_start=2121 - _globals['_JOBASSIGNMENT']._serialized_end=2204 + _globals['_JOB_ATTRIBUTESENTRY']._loaded_options = None + _globals['_JOB_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_JOB'].fields_by_name['dispatch_id']._loaded_options = None + _globals['_JOB'].fields_by_name['dispatch_id']._serialized_options = b'\272P\ndispatchID' + _globals['_JOB'].fields_by_name['namespace']._loaded_options = None + _globals['_JOB'].fields_by_name['namespace']._serialized_options = b'\030\001' + _globals['_JOB'].fields_by_name['metadata']._loaded_options = None + _globals['_JOB'].fields_by_name['metadata']._serialized_options = b'\300P\001' + _globals['_JOB'].fields_by_name['attributes']._loaded_options = None + _globals['_JOB'].fields_by_name['attributes']._serialized_options = b'\300P\001' + _globals['_JOBSTATE'].fields_by_name['worker_id']._loaded_options = None + _globals['_JOBSTATE'].fields_by_name['worker_id']._serialized_options = b'\272P\010workerID' + _globals['_JOBSTATE'].fields_by_name['agent_id']._loaded_options = None + _globals['_JOBSTATE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_REGISTERWORKERRESPONSE'].fields_by_name['worker_id']._loaded_options = None + _globals['_REGISTERWORKERRESPONSE'].fields_by_name['worker_id']._serialized_options = b'\272P\010workerID' + _globals['_AVAILABILITYRESPONSE_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_AVAILABILITYRESPONSE_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_AVAILABILITYRESPONSE'].fields_by_name['job_id']._loaded_options = None + _globals['_AVAILABILITYRESPONSE'].fields_by_name['job_id']._serialized_options = b'\272P\005jobID' + _globals['_UPDATEJOBSTATUS'].fields_by_name['job_id']._loaded_options = None + _globals['_UPDATEJOBSTATUS'].fields_by_name['job_id']._serialized_options = b'\272P\005jobID' + _globals['_JOBTERMINATION'].fields_by_name['job_id']._loaded_options = None + _globals['_JOBTERMINATION'].fields_by_name['job_id']._serialized_options = b'\272P\005jobID' + _globals['_JOBTYPE']._serialized_start=2750 + _globals['_JOBTYPE']._serialized_end=2810 + _globals['_WORKERSTATUS']._serialized_start=2812 + _globals['_WORKERSTATUS']._serialized_end=2857 + _globals['_JOBSTATUS']._serialized_start=2859 + _globals['_JOBSTATUS']._serialized_end=2933 + _globals['_JOB']._serialized_start=77 + _globals['_JOB']._serialized_end=537 + _globals['_JOB_ATTRIBUTESENTRY']._serialized_start=472 + _globals['_JOB_ATTRIBUTESENTRY']._serialized_end=521 + _globals['_JOBSTATE']._serialized_start=540 + _globals['_JOBSTATE']._serialized_end=751 + _globals['_WORKERMESSAGE']._serialized_start=754 + _globals['_WORKERMESSAGE']._serialized_end=1130 + _globals['_SERVERMESSAGE']._serialized_start=1133 + _globals['_SERVERMESSAGE']._serialized_end=1397 + _globals['_SIMULATEJOBREQUEST']._serialized_start=1400 + _globals['_SIMULATEJOBREQUEST']._serialized_end=1528 + _globals['_WORKERPING']._serialized_start=1530 + _globals['_WORKERPING']._serialized_end=1561 + _globals['_WORKERPONG']._serialized_start=1563 + _globals['_WORKERPONG']._serialized_end=1618 + _globals['_REGISTERWORKERREQUEST']._serialized_start=1621 + _globals['_REGISTERWORKERREQUEST']._serialized_end=1855 + _globals['_REGISTERWORKERRESPONSE']._serialized_start=1857 + _globals['_REGISTERWORKERRESPONSE']._serialized_end=1955 + _globals['_MIGRATEJOBREQUEST']._serialized_start=1957 + _globals['_MIGRATEJOBREQUEST']._serialized_end=1993 + _globals['_AVAILABILITYREQUEST']._serialized_start=1995 + _globals['_AVAILABILITYREQUEST']._serialized_end=2061 + _globals['_AVAILABILITYRESPONSE']._serialized_start=2064 + _globals['_AVAILABILITYRESPONSE']._serialized_end=2413 + _globals['_AVAILABILITYRESPONSE_PARTICIPANTATTRIBUTESENTRY']._serialized_start=2353 + _globals['_AVAILABILITYRESPONSE_PARTICIPANTATTRIBUTESENTRY']._serialized_end=2413 + _globals['_UPDATEJOBSTATUS']._serialized_start=2415 + _globals['_UPDATEJOBSTATUS']._serialized_end=2509 + _globals['_UPDATEWORKERSTATUS']._serialized_start=2511 + _globals['_UPDATEWORKERSTATUS']._serialized_end=2619 + _globals['_JOBASSIGNMENT']._serialized_start=2621 + _globals['_JOBASSIGNMENT']._serialized_end=2704 + _globals['_JOBTERMINATION']._serialized_start=2706 + _globals['_JOBTERMINATION']._serialized_end=2748 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent.pyi b/livekit-protocol/livekit/protocol/agent.pyi index 502f695b..5c7b1dff 100644 --- a/livekit-protocol/livekit/protocol/agent.pyi +++ b/livekit-protocol/livekit/protocol/agent.pyi @@ -1,8 +1,10 @@ from . import models as _models +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message -from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union DESCRIPTOR: _descriptor.FileDescriptor @@ -10,6 +12,7 @@ class JobType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () JT_ROOM: _ClassVar[JobType] JT_PUBLISHER: _ClassVar[JobType] + JT_PARTICIPANT: _ClassVar[JobType] class WorkerStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -18,56 +21,76 @@ class WorkerStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): class JobStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () - JS_UNKNOWN: _ClassVar[JobStatus] + JS_PENDING: _ClassVar[JobStatus] + JS_RUNNING: _ClassVar[JobStatus] JS_SUCCESS: _ClassVar[JobStatus] JS_FAILED: _ClassVar[JobStatus] JT_ROOM: JobType JT_PUBLISHER: JobType +JT_PARTICIPANT: JobType WS_AVAILABLE: WorkerStatus WS_FULL: WorkerStatus -JS_UNKNOWN: JobStatus +JS_PENDING: JobStatus +JS_RUNNING: JobStatus JS_SUCCESS: JobStatus JS_FAILED: JobStatus -class WorkerInfo(_message.Message): - __slots__ = ("id", "namespace", "version", "name", "type", "allowed_permissions") - ID_FIELD_NUMBER: _ClassVar[int] - NAMESPACE_FIELD_NUMBER: _ClassVar[int] - VERSION_FIELD_NUMBER: _ClassVar[int] - NAME_FIELD_NUMBER: _ClassVar[int] - TYPE_FIELD_NUMBER: _ClassVar[int] - ALLOWED_PERMISSIONS_FIELD_NUMBER: _ClassVar[int] - id: str - namespace: str - version: str - name: str - type: JobType - allowed_permissions: _models.ParticipantPermission - def __init__(self, id: _Optional[str] = ..., namespace: _Optional[str] = ..., version: _Optional[str] = ..., name: _Optional[str] = ..., type: _Optional[_Union[JobType, str]] = ..., allowed_permissions: _Optional[_Union[_models.ParticipantPermission, _Mapping]] = ...) -> None: ... - -class AgentInfo(_message.Message): - __slots__ = ("id", "name", "version") - ID_FIELD_NUMBER: _ClassVar[int] - NAME_FIELD_NUMBER: _ClassVar[int] - VERSION_FIELD_NUMBER: _ClassVar[int] - id: str - name: str - version: str - def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., version: _Optional[str] = ...) -> None: ... - class Job(_message.Message): - __slots__ = ("id", "type", "room", "participant", "namespace") + __slots__ = ("id", "dispatch_id", "type", "room", "participant", "namespace", "metadata", "agent_name", "state", "enable_recording", "deployment", "attributes", "enable_redaction") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... ID_FIELD_NUMBER: _ClassVar[int] + DISPATCH_ID_FIELD_NUMBER: _ClassVar[int] TYPE_FIELD_NUMBER: _ClassVar[int] ROOM_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_FIELD_NUMBER: _ClassVar[int] NAMESPACE_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + STATE_FIELD_NUMBER: _ClassVar[int] + ENABLE_RECORDING_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ENABLE_REDACTION_FIELD_NUMBER: _ClassVar[int] id: str + dispatch_id: str type: JobType room: _models.Room participant: _models.ParticipantInfo namespace: str - def __init__(self, id: _Optional[str] = ..., type: _Optional[_Union[JobType, str]] = ..., room: _Optional[_Union[_models.Room, _Mapping]] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., namespace: _Optional[str] = ...) -> None: ... + metadata: str + agent_name: str + state: JobState + enable_recording: bool + deployment: str + attributes: _containers.ScalarMap[str, str] + enable_redaction: bool + def __init__(self, id: _Optional[str] = ..., dispatch_id: _Optional[str] = ..., type: _Optional[_Union[JobType, str]] = ..., room: _Optional[_Union[_models.Room, _Mapping]] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., namespace: _Optional[str] = ..., metadata: _Optional[str] = ..., agent_name: _Optional[str] = ..., state: _Optional[_Union[JobState, _Mapping]] = ..., enable_recording: bool = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ..., enable_redaction: bool = ...) -> None: ... + +class JobState(_message.Message): + __slots__ = ("status", "error", "started_at", "ended_at", "updated_at", "participant_identity", "worker_id", "agent_id") + STATUS_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + STARTED_AT_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + WORKER_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + status: JobStatus + error: str + started_at: int + ended_at: int + updated_at: int + participant_identity: str + worker_id: str + agent_id: str + def __init__(self, status: _Optional[_Union[JobStatus, str]] = ..., error: _Optional[str] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., updated_at: _Optional[int] = ..., participant_identity: _Optional[str] = ..., worker_id: _Optional[str] = ..., agent_id: _Optional[str] = ...) -> None: ... class WorkerMessage(_message.Message): __slots__ = ("register", "availability", "update_worker", "update_job", "ping", "simulate_job", "migrate_job") @@ -88,16 +111,18 @@ class WorkerMessage(_message.Message): def __init__(self, register: _Optional[_Union[RegisterWorkerRequest, _Mapping]] = ..., availability: _Optional[_Union[AvailabilityResponse, _Mapping]] = ..., update_worker: _Optional[_Union[UpdateWorkerStatus, _Mapping]] = ..., update_job: _Optional[_Union[UpdateJobStatus, _Mapping]] = ..., ping: _Optional[_Union[WorkerPing, _Mapping]] = ..., simulate_job: _Optional[_Union[SimulateJobRequest, _Mapping]] = ..., migrate_job: _Optional[_Union[MigrateJobRequest, _Mapping]] = ...) -> None: ... class ServerMessage(_message.Message): - __slots__ = ("register", "availability", "assignment", "pong") + __slots__ = ("register", "availability", "assignment", "termination", "pong") REGISTER_FIELD_NUMBER: _ClassVar[int] AVAILABILITY_FIELD_NUMBER: _ClassVar[int] ASSIGNMENT_FIELD_NUMBER: _ClassVar[int] + TERMINATION_FIELD_NUMBER: _ClassVar[int] PONG_FIELD_NUMBER: _ClassVar[int] register: RegisterWorkerResponse availability: AvailabilityRequest assignment: JobAssignment + termination: JobTermination pong: WorkerPong - def __init__(self, register: _Optional[_Union[RegisterWorkerResponse, _Mapping]] = ..., availability: _Optional[_Union[AvailabilityRequest, _Mapping]] = ..., assignment: _Optional[_Union[JobAssignment, _Mapping]] = ..., pong: _Optional[_Union[WorkerPong, _Mapping]] = ...) -> None: ... + def __init__(self, register: _Optional[_Union[RegisterWorkerResponse, _Mapping]] = ..., availability: _Optional[_Union[AvailabilityRequest, _Mapping]] = ..., assignment: _Optional[_Union[JobAssignment, _Mapping]] = ..., termination: _Optional[_Union[JobTermination, _Mapping]] = ..., pong: _Optional[_Union[WorkerPong, _Mapping]] = ...) -> None: ... class SimulateJobRequest(_message.Message): __slots__ = ("type", "room", "participant") @@ -124,20 +149,22 @@ class WorkerPong(_message.Message): def __init__(self, last_timestamp: _Optional[int] = ..., timestamp: _Optional[int] = ...) -> None: ... class RegisterWorkerRequest(_message.Message): - __slots__ = ("type", "version", "name", "ping_interval", "namespace", "allowed_permissions") + __slots__ = ("type", "agent_name", "version", "ping_interval", "namespace", "allowed_permissions", "deployment") TYPE_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] - NAME_FIELD_NUMBER: _ClassVar[int] PING_INTERVAL_FIELD_NUMBER: _ClassVar[int] NAMESPACE_FIELD_NUMBER: _ClassVar[int] ALLOWED_PERMISSIONS_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] type: JobType + agent_name: str version: str - name: str ping_interval: int namespace: str allowed_permissions: _models.ParticipantPermission - def __init__(self, type: _Optional[_Union[JobType, str]] = ..., version: _Optional[str] = ..., name: _Optional[str] = ..., ping_interval: _Optional[int] = ..., namespace: _Optional[str] = ..., allowed_permissions: _Optional[_Union[_models.ParticipantPermission, _Mapping]] = ...) -> None: ... + deployment: str + def __init__(self, type: _Optional[_Union[JobType, str]] = ..., agent_name: _Optional[str] = ..., version: _Optional[str] = ..., ping_interval: _Optional[int] = ..., namespace: _Optional[str] = ..., allowed_permissions: _Optional[_Union[_models.ParticipantPermission, _Mapping]] = ..., deployment: _Optional[str] = ...) -> None: ... class RegisterWorkerResponse(_message.Message): __slots__ = ("worker_id", "server_info") @@ -148,10 +175,10 @@ class RegisterWorkerResponse(_message.Message): def __init__(self, worker_id: _Optional[str] = ..., server_info: _Optional[_Union[_models.ServerInfo, _Mapping]] = ...) -> None: ... class MigrateJobRequest(_message.Message): - __slots__ = ("job_id",) - JOB_ID_FIELD_NUMBER: _ClassVar[int] - job_id: str - def __init__(self, job_id: _Optional[str] = ...) -> None: ... + __slots__ = ("job_ids",) + JOB_IDS_FIELD_NUMBER: _ClassVar[int] + job_ids: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, job_ids: _Optional[_Iterable[str]] = ...) -> None: ... class AvailabilityRequest(_message.Message): __slots__ = ("job", "resuming") @@ -162,44 +189,51 @@ class AvailabilityRequest(_message.Message): def __init__(self, job: _Optional[_Union[Job, _Mapping]] = ..., resuming: bool = ...) -> None: ... class AvailabilityResponse(_message.Message): - __slots__ = ("job_id", "available", "supports_resume", "participant_name", "participant_identity", "participant_metadata") + __slots__ = ("job_id", "available", "supports_resume", "terminate", "participant_name", "participant_identity", "participant_metadata", "participant_attributes") + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... JOB_ID_FIELD_NUMBER: _ClassVar[int] AVAILABLE_FIELD_NUMBER: _ClassVar[int] SUPPORTS_RESUME_FIELD_NUMBER: _ClassVar[int] + TERMINATE_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_NAME_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_METADATA_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] job_id: str available: bool supports_resume: bool + terminate: bool participant_name: str participant_identity: str participant_metadata: str - def __init__(self, job_id: _Optional[str] = ..., available: bool = ..., supports_resume: bool = ..., participant_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_metadata: _Optional[str] = ...) -> None: ... + participant_attributes: _containers.ScalarMap[str, str] + def __init__(self, job_id: _Optional[str] = ..., available: bool = ..., supports_resume: bool = ..., terminate: bool = ..., participant_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... class UpdateJobStatus(_message.Message): - __slots__ = ("job_id", "status", "error", "metadata", "load") + __slots__ = ("job_id", "status", "error") JOB_ID_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] ERROR_FIELD_NUMBER: _ClassVar[int] - METADATA_FIELD_NUMBER: _ClassVar[int] - LOAD_FIELD_NUMBER: _ClassVar[int] job_id: str status: JobStatus error: str - metadata: str - load: float - def __init__(self, job_id: _Optional[str] = ..., status: _Optional[_Union[JobStatus, str]] = ..., error: _Optional[str] = ..., metadata: _Optional[str] = ..., load: _Optional[float] = ...) -> None: ... + def __init__(self, job_id: _Optional[str] = ..., status: _Optional[_Union[JobStatus, str]] = ..., error: _Optional[str] = ...) -> None: ... class UpdateWorkerStatus(_message.Message): - __slots__ = ("status", "metadata", "load") + __slots__ = ("status", "load", "job_count") STATUS_FIELD_NUMBER: _ClassVar[int] - METADATA_FIELD_NUMBER: _ClassVar[int] LOAD_FIELD_NUMBER: _ClassVar[int] + JOB_COUNT_FIELD_NUMBER: _ClassVar[int] status: WorkerStatus - metadata: str load: float - def __init__(self, status: _Optional[_Union[WorkerStatus, str]] = ..., metadata: _Optional[str] = ..., load: _Optional[float] = ...) -> None: ... + job_count: int + def __init__(self, status: _Optional[_Union[WorkerStatus, str]] = ..., load: _Optional[float] = ..., job_count: _Optional[int] = ...) -> None: ... class JobAssignment(_message.Message): __slots__ = ("job", "url", "token") @@ -210,3 +244,9 @@ class JobAssignment(_message.Message): url: str token: str def __init__(self, job: _Optional[_Union[Job, _Mapping]] = ..., url: _Optional[str] = ..., token: _Optional[str] = ...) -> None: ... + +class JobTermination(_message.Message): + __slots__ = ("job_id",) + JOB_ID_FIELD_NUMBER: _ClassVar[int] + job_id: str + def __init__(self, job_id: _Optional[str] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_dispatch.py b/livekit-protocol/livekit/protocol/agent_dispatch.py new file mode 100644 index 00000000..840dbb21 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_dispatch.py @@ -0,0 +1,83 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_agent_dispatch.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_agent_dispatch.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import agent as _agent_ +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1clivekit_agent_dispatch.proto\x12\x07livekit\x1a\x13livekit_agent.proto\x1a\x14logger/options.proto\"\x9d\x02\n\x1a\x43reateAgentDispatchRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x0c\n\x04room\x18\x02 \x01(\t\x12\x15\n\x08metadata\x18\x03 \x01(\tB\x03\xc0P\x01\x12\x31\n\x0erestart_policy\x18\x04 \x01(\x0e\x32\x19.livekit.JobRestartPolicy\x12\x12\n\ndeployment\x18\x05 \x01(\t\x12L\n\nattributes\x18\x06 \x03(\x0b\x32\x33.livekit.CreateAgentDispatchRequest.AttributesEntryB\x03\xc0P\x01\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xfd\x01\n\x11RoomAgentDispatch\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x15\n\x08metadata\x18\x02 \x01(\tB\x03\xc0P\x01\x12\x31\n\x0erestart_policy\x18\x03 \x01(\x0e\x32\x19.livekit.JobRestartPolicy\x12\x12\n\ndeployment\x18\x04 \x01(\t\x12\x43\n\nattributes\x18\x05 \x03(\x0b\x32*.livekit.RoomAgentDispatch.AttributesEntryB\x03\xc0P\x01\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"N\n\x1a\x44\x65leteAgentDispatchRequest\x12\"\n\x0b\x64ispatch_id\x18\x01 \x01(\tB\r\xbaP\ndispatchID\x12\x0c\n\x04room\x18\x02 \x01(\t\"L\n\x18ListAgentDispatchRequest\x12\"\n\x0b\x64ispatch_id\x18\x01 \x01(\tB\r\xbaP\ndispatchID\x12\x0c\n\x04room\x18\x02 \x01(\t\"M\n\x19ListAgentDispatchResponse\x12\x30\n\x10\x61gent_dispatches\x18\x01 \x03(\x0b\x32\x16.livekit.AgentDispatch\"\xbb\x02\n\rAgentDispatch\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x0c\n\x04room\x18\x03 \x01(\t\x12\x15\n\x08metadata\x18\x04 \x01(\tB\x03\xc0P\x01\x12*\n\x05state\x18\x05 \x01(\x0b\x32\x1b.livekit.AgentDispatchState\x12\x31\n\x0erestart_policy\x18\x06 \x01(\x0e\x32\x19.livekit.JobRestartPolicy\x12\x12\n\ndeployment\x18\x07 \x01(\t\x12?\n\nattributes\x18\x08 \x03(\x0b\x32&.livekit.AgentDispatch.AttributesEntryB\x03\xc0P\x01\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"X\n\x12\x41gentDispatchState\x12\x1a\n\x04jobs\x18\x01 \x03(\x0b\x32\x0c.livekit.Job\x12\x12\n\ncreated_at\x18\x02 \x01(\x03\x12\x12\n\ndeleted_at\x18\x03 \x01(\x03*5\n\x10JobRestartPolicy\x12\x12\n\x0eJRP_ON_FAILURE\x10\x00\x12\r\n\tJRP_NEVER\x10\x01\x32\x8b\x02\n\x14\x41gentDispatchService\x12M\n\x0e\x43reateDispatch\x12#.livekit.CreateAgentDispatchRequest\x1a\x16.livekit.AgentDispatch\x12M\n\x0e\x44\x65leteDispatch\x12#.livekit.DeleteAgentDispatchRequest\x1a\x16.livekit.AgentDispatch\x12U\n\x0cListDispatch\x12!.livekit.ListAgentDispatchRequest\x1a\".livekit.ListAgentDispatchResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_dispatch', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_CREATEAGENTDISPATCHREQUEST_ATTRIBUTESENTRY']._loaded_options = None + _globals['_CREATEAGENTDISPATCHREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_CREATEAGENTDISPATCHREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_CREATEAGENTDISPATCHREQUEST'].fields_by_name['metadata']._serialized_options = b'\300P\001' + _globals['_CREATEAGENTDISPATCHREQUEST'].fields_by_name['attributes']._loaded_options = None + _globals['_CREATEAGENTDISPATCHREQUEST'].fields_by_name['attributes']._serialized_options = b'\300P\001' + _globals['_ROOMAGENTDISPATCH_ATTRIBUTESENTRY']._loaded_options = None + _globals['_ROOMAGENTDISPATCH_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_ROOMAGENTDISPATCH'].fields_by_name['metadata']._loaded_options = None + _globals['_ROOMAGENTDISPATCH'].fields_by_name['metadata']._serialized_options = b'\300P\001' + _globals['_ROOMAGENTDISPATCH'].fields_by_name['attributes']._loaded_options = None + _globals['_ROOMAGENTDISPATCH'].fields_by_name['attributes']._serialized_options = b'\300P\001' + _globals['_DELETEAGENTDISPATCHREQUEST'].fields_by_name['dispatch_id']._loaded_options = None + _globals['_DELETEAGENTDISPATCHREQUEST'].fields_by_name['dispatch_id']._serialized_options = b'\272P\ndispatchID' + _globals['_LISTAGENTDISPATCHREQUEST'].fields_by_name['dispatch_id']._loaded_options = None + _globals['_LISTAGENTDISPATCHREQUEST'].fields_by_name['dispatch_id']._serialized_options = b'\272P\ndispatchID' + _globals['_AGENTDISPATCH_ATTRIBUTESENTRY']._loaded_options = None + _globals['_AGENTDISPATCH_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_AGENTDISPATCH'].fields_by_name['metadata']._loaded_options = None + _globals['_AGENTDISPATCH'].fields_by_name['metadata']._serialized_options = b'\300P\001' + _globals['_AGENTDISPATCH'].fields_by_name['attributes']._loaded_options = None + _globals['_AGENTDISPATCH'].fields_by_name['attributes']._serialized_options = b'\300P\001' + _globals['_JOBRESTARTPOLICY']._serialized_start=1273 + _globals['_JOBRESTARTPOLICY']._serialized_end=1326 + _globals['_CREATEAGENTDISPATCHREQUEST']._serialized_start=85 + _globals['_CREATEAGENTDISPATCHREQUEST']._serialized_end=370 + _globals['_CREATEAGENTDISPATCHREQUEST_ATTRIBUTESENTRY']._serialized_start=321 + _globals['_CREATEAGENTDISPATCHREQUEST_ATTRIBUTESENTRY']._serialized_end=370 + _globals['_ROOMAGENTDISPATCH']._serialized_start=373 + _globals['_ROOMAGENTDISPATCH']._serialized_end=626 + _globals['_ROOMAGENTDISPATCH_ATTRIBUTESENTRY']._serialized_start=321 + _globals['_ROOMAGENTDISPATCH_ATTRIBUTESENTRY']._serialized_end=370 + _globals['_DELETEAGENTDISPATCHREQUEST']._serialized_start=628 + _globals['_DELETEAGENTDISPATCHREQUEST']._serialized_end=706 + _globals['_LISTAGENTDISPATCHREQUEST']._serialized_start=708 + _globals['_LISTAGENTDISPATCHREQUEST']._serialized_end=784 + _globals['_LISTAGENTDISPATCHRESPONSE']._serialized_start=786 + _globals['_LISTAGENTDISPATCHRESPONSE']._serialized_end=863 + _globals['_AGENTDISPATCH']._serialized_start=866 + _globals['_AGENTDISPATCH']._serialized_end=1181 + _globals['_AGENTDISPATCH_ATTRIBUTESENTRY']._serialized_start=321 + _globals['_AGENTDISPATCH_ATTRIBUTESENTRY']._serialized_end=370 + _globals['_AGENTDISPATCHSTATE']._serialized_start=1183 + _globals['_AGENTDISPATCHSTATE']._serialized_end=1271 + _globals['_AGENTDISPATCHSERVICE']._serialized_start=1329 + _globals['_AGENTDISPATCHSERVICE']._serialized_end=1596 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_dispatch.pyi b/livekit-protocol/livekit/protocol/agent_dispatch.pyi new file mode 100644 index 00000000..214cdefe --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_dispatch.pyi @@ -0,0 +1,119 @@ +from . import agent as _agent +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class JobRestartPolicy(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + JRP_ON_FAILURE: _ClassVar[JobRestartPolicy] + JRP_NEVER: _ClassVar[JobRestartPolicy] +JRP_ON_FAILURE: JobRestartPolicy +JRP_NEVER: JobRestartPolicy + +class CreateAgentDispatchRequest(_message.Message): + __slots__ = ("agent_name", "room", "metadata", "restart_policy", "deployment", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + RESTART_POLICY_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + agent_name: str + room: str + metadata: str + restart_policy: JobRestartPolicy + deployment: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, agent_name: _Optional[str] = ..., room: _Optional[str] = ..., metadata: _Optional[str] = ..., restart_policy: _Optional[_Union[JobRestartPolicy, str]] = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class RoomAgentDispatch(_message.Message): + __slots__ = ("agent_name", "metadata", "restart_policy", "deployment", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + RESTART_POLICY_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + agent_name: str + metadata: str + restart_policy: JobRestartPolicy + deployment: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, agent_name: _Optional[str] = ..., metadata: _Optional[str] = ..., restart_policy: _Optional[_Union[JobRestartPolicy, str]] = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class DeleteAgentDispatchRequest(_message.Message): + __slots__ = ("dispatch_id", "room") + DISPATCH_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_FIELD_NUMBER: _ClassVar[int] + dispatch_id: str + room: str + def __init__(self, dispatch_id: _Optional[str] = ..., room: _Optional[str] = ...) -> None: ... + +class ListAgentDispatchRequest(_message.Message): + __slots__ = ("dispatch_id", "room") + DISPATCH_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_FIELD_NUMBER: _ClassVar[int] + dispatch_id: str + room: str + def __init__(self, dispatch_id: _Optional[str] = ..., room: _Optional[str] = ...) -> None: ... + +class ListAgentDispatchResponse(_message.Message): + __slots__ = ("agent_dispatches",) + AGENT_DISPATCHES_FIELD_NUMBER: _ClassVar[int] + agent_dispatches: _containers.RepeatedCompositeFieldContainer[AgentDispatch] + def __init__(self, agent_dispatches: _Optional[_Iterable[_Union[AgentDispatch, _Mapping]]] = ...) -> None: ... + +class AgentDispatch(_message.Message): + __slots__ = ("id", "agent_name", "room", "metadata", "state", "restart_policy", "deployment", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + STATE_FIELD_NUMBER: _ClassVar[int] + RESTART_POLICY_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + id: str + agent_name: str + room: str + metadata: str + state: AgentDispatchState + restart_policy: JobRestartPolicy + deployment: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, id: _Optional[str] = ..., agent_name: _Optional[str] = ..., room: _Optional[str] = ..., metadata: _Optional[str] = ..., state: _Optional[_Union[AgentDispatchState, _Mapping]] = ..., restart_policy: _Optional[_Union[JobRestartPolicy, str]] = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class AgentDispatchState(_message.Message): + __slots__ = ("jobs", "created_at", "deleted_at") + JOBS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + DELETED_AT_FIELD_NUMBER: _ClassVar[int] + jobs: _containers.RepeatedCompositeFieldContainer[_agent.Job] + created_at: int + deleted_at: int + def __init__(self, jobs: _Optional[_Iterable[_Union[_agent.Job, _Mapping]]] = ..., created_at: _Optional[int] = ..., deleted_at: _Optional[int] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_pb/__init__.py b/livekit-protocol/livekit/protocol/agent_pb/__init__.py new file mode 100644 index 00000000..a2404969 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/__init__.py @@ -0,0 +1 @@ +from . import agent_dev, agent_session, agent_text, agent_inference diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_dev.py b/livekit-protocol/livekit/protocol/agent_pb/agent_dev.py new file mode 100644 index 00000000..9df7d94c --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_dev.py @@ -0,0 +1,45 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agent/livekit_agent_dev.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'agent/livekit_agent_dev.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1d\x61gent/livekit_agent_dev.proto\x12\rlivekit.agent\"\xee\x01\n\x0f\x41gentDevMessage\x12M\n\x18get_running_jobs_request\x18\x01 \x01(\x0b\x32).livekit.agent.GetRunningAgentJobsRequestH\x00\x12O\n\x19get_running_jobs_response\x18\x02 \x01(\x0b\x32*.livekit.agent.GetRunningAgentJobsResponseH\x00\x12\x30\n\x0bserver_info\x18\x03 \x01(\x0b\x32\x19.livekit.agent.ServerInfoH\x00\x42\t\n\x07message\"-\n\nServerInfo\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\"\x1c\n\x1aGetRunningAgentJobsRequest\"O\n\x1bGetRunningAgentJobsResponse\x12\x30\n\x04jobs\x18\x01 \x03(\x0b\x32\".livekit.agent.RunningAgentJobInfo\"\xaa\x01\n\x13RunningAgentJobInfo\x12\x0b\n\x03job\x18\x01 \x01(\x0c\x12\x13\n\x0b\x61\x63\x63\x65pt_name\x18\x02 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65pt_identity\x18\x03 \x01(\t\x12\x17\n\x0f\x61\x63\x63\x65pt_metadata\x18\x04 \x01(\t\x12\x0b\n\x03url\x18\x05 \x01(\t\x12\r\n\x05token\x18\x06 \x01(\t\x12\x11\n\tworker_id\x18\x07 \x01(\t\x12\x10\n\x08mock_job\x18\x08 \x01(\x08\x42+Z)github.com/livekit/protocol/livekit/agentb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent.agent_dev', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z)github.com/livekit/protocol/livekit/agent' + _globals['_AGENTDEVMESSAGE']._serialized_start=49 + _globals['_AGENTDEVMESSAGE']._serialized_end=287 + _globals['_SERVERINFO']._serialized_start=289 + _globals['_SERVERINFO']._serialized_end=334 + _globals['_GETRUNNINGAGENTJOBSREQUEST']._serialized_start=336 + _globals['_GETRUNNINGAGENTJOBSREQUEST']._serialized_end=364 + _globals['_GETRUNNINGAGENTJOBSRESPONSE']._serialized_start=366 + _globals['_GETRUNNINGAGENTJOBSRESPONSE']._serialized_end=445 + _globals['_RUNNINGAGENTJOBINFO']._serialized_start=448 + _globals['_RUNNINGAGENTJOBINFO']._serialized_end=618 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_dev.pyi b/livekit-protocol/livekit/protocol/agent_pb/agent_dev.pyi new file mode 100644 index 00000000..cf588df1 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_dev.pyi @@ -0,0 +1,54 @@ +from google.protobuf.internal import containers as _containers +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class AgentDevMessage(_message.Message): + __slots__ = ("get_running_jobs_request", "get_running_jobs_response", "server_info") + GET_RUNNING_JOBS_REQUEST_FIELD_NUMBER: _ClassVar[int] + GET_RUNNING_JOBS_RESPONSE_FIELD_NUMBER: _ClassVar[int] + SERVER_INFO_FIELD_NUMBER: _ClassVar[int] + get_running_jobs_request: GetRunningAgentJobsRequest + get_running_jobs_response: GetRunningAgentJobsResponse + server_info: ServerInfo + def __init__(self, get_running_jobs_request: _Optional[_Union[GetRunningAgentJobsRequest, _Mapping]] = ..., get_running_jobs_response: _Optional[_Union[GetRunningAgentJobsResponse, _Mapping]] = ..., server_info: _Optional[_Union[ServerInfo, _Mapping]] = ...) -> None: ... + +class ServerInfo(_message.Message): + __slots__ = ("agent_name", "url") + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + agent_name: str + url: str + def __init__(self, agent_name: _Optional[str] = ..., url: _Optional[str] = ...) -> None: ... + +class GetRunningAgentJobsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class GetRunningAgentJobsResponse(_message.Message): + __slots__ = ("jobs",) + JOBS_FIELD_NUMBER: _ClassVar[int] + jobs: _containers.RepeatedCompositeFieldContainer[RunningAgentJobInfo] + def __init__(self, jobs: _Optional[_Iterable[_Union[RunningAgentJobInfo, _Mapping]]] = ...) -> None: ... + +class RunningAgentJobInfo(_message.Message): + __slots__ = ("job", "accept_name", "accept_identity", "accept_metadata", "url", "token", "worker_id", "mock_job") + JOB_FIELD_NUMBER: _ClassVar[int] + ACCEPT_NAME_FIELD_NUMBER: _ClassVar[int] + ACCEPT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + ACCEPT_METADATA_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + TOKEN_FIELD_NUMBER: _ClassVar[int] + WORKER_ID_FIELD_NUMBER: _ClassVar[int] + MOCK_JOB_FIELD_NUMBER: _ClassVar[int] + job: bytes + accept_name: str + accept_identity: str + accept_metadata: str + url: str + token: str + worker_id: str + mock_job: bool + def __init__(self, job: _Optional[bytes] = ..., accept_name: _Optional[str] = ..., accept_identity: _Optional[str] = ..., accept_metadata: _Optional[str] = ..., url: _Optional[str] = ..., token: _Optional[str] = ..., worker_id: _Optional[str] = ..., mock_job: bool = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_inference.py b/livekit-protocol/livekit/protocol/agent_pb/agent_inference.py new file mode 100644 index 00000000..c834163b --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_inference.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agent/livekit_agent_inference.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'agent/livekit_agent_inference.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import agent_session as agent_dot__agent__session_ +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n#agent/livekit_agent_inference.proto\x12\rlivekit.agent\x1a!agent/livekit_agent_session.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1fgoogle/protobuf/timestamp.proto\"\xe1\x01\n\x0fSessionSettings\x12\x13\n\x0bsample_rate\x18\x01 \x01(\r\x12.\n\x08\x65ncoding\x18\x02 \x01(\x0e\x32\x1c.livekit.agent.AudioEncoding\x12\x32\n\x0c\x65ot_settings\x18\x03 \x01(\x0b\x32\x1a.livekit.agent.EotSettingsH\x00\x12\x44\n\x15interruption_settings\x18\x04 \x01(\x0b\x32#.livekit.agent.InterruptionSettingsH\x00\x42\x0f\n\rtype_settings\"/\n\x0eInferenceError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x0c\n\x04\x63ode\x18\x02 \x01(\r\"D\n\x0b\x45otSettings\x12\x35\n\x12\x64\x65tection_interval\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\"\xe5\x01\n\x14InterruptionSettings\x12\x11\n\tthreshold\x18\x01 \x01(\x02\x12\x12\n\nmin_frames\x18\x02 \x01(\r\x12\x35\n\x12max_audio_duration\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x38\n\x15\x61udio_prefix_duration\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x35\n\x12\x64\x65tection_interval\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\"A\n\rSessionCreate\x12\x30\n\x08settings\x18\x01 \x01(\x0b\x32\x1e.livekit.agent.SessionSettings\"`\n\nInputAudio\x12\r\n\x05\x61udio\x18\x01 \x01(\x0c\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x13\n\x0bnum_samples\x18\x03 \x01(\r\"C\n\x13\x45otInputChatContext\x12,\n\x08messages\x18\x01 \x03(\x0b\x32\x1a.livekit.agent.ChatMessage\"\x0e\n\x0cSessionFlush\"\x0e\n\x0cSessionClose\"$\n\x0eInferenceStart\x12\x12\n\nrequest_id\x18\x01 \x01(\t\"#\n\rInferenceStop\x12\x12\n\nrequest_id\x18\x01 \x01(\t\"\r\n\x0b\x42ufferStart\"\x0c\n\nBufferStop\"\xbe\x04\n\rClientMessage\x12.\n\ncreated_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x36\n\x0esession_create\x18\x02 \x01(\x0b\x32\x1c.livekit.agent.SessionCreateH\x00\x12\x30\n\x0binput_audio\x18\x03 \x01(\x0b\x32\x19.livekit.agent.InputAudioH\x00\x12\x34\n\rsession_flush\x18\x04 \x01(\x0b\x32\x1b.livekit.agent.SessionFlushH\x00\x12\x34\n\rsession_close\x18\x05 \x01(\x0b\x32\x1b.livekit.agent.SessionCloseH\x00\x12\x38\n\x0finference_start\x18\x06 \x01(\x0b\x32\x1d.livekit.agent.InferenceStartH\x00\x12\x36\n\x0einference_stop\x18\x07 \x01(\x0b\x32\x1c.livekit.agent.InferenceStopH\x00\x12\x32\n\x0c\x62uffer_start\x18\x08 \x01(\x0b\x32\x1a.livekit.agent.BufferStartH\x00\x12\x30\n\x0b\x62uffer_stop\x18\t \x01(\x0b\x32\x19.livekit.agent.BufferStopH\x00\x12\x44\n\x16\x65ot_input_chat_context\x18\n \x01(\x0b\x32\".livekit.agent.EotInputChatContextH\x00\x42\t\n\x07message\"\x81\x01\n\x13\x45otInferenceRequest\x12\r\n\x05\x61udio\x18\x01 \x01(\x0c\x12\x16\n\x0e\x61ssistant_text\x18\x02 \x01(\t\x12.\n\x08\x65ncoding\x18\x03 \x01(\x0e\x32\x1c.livekit.agent.AudioEncoding\x12\x13\n\x0bsample_rate\x18\x04 \x01(\r\"r\n\x1cInterruptionInferenceRequest\x12\r\n\x05\x61udio\x18\x01 \x01(\x0c\x12.\n\x08\x65ncoding\x18\x02 \x01(\x0e\x32\x1c.livekit.agent.AudioEncoding\x12\x13\n\x0bsample_rate\x18\x03 \x01(\r\"\xb9\x01\n\x10InferenceRequest\x12\x43\n\x15\x65ot_inference_request\x18\x01 \x01(\x0b\x32\".livekit.agent.EotInferenceRequestH\x00\x12U\n\x1einterruption_inference_request\x18\x02 \x01(\x0b\x32+.livekit.agent.InterruptionInferenceRequestH\x00\x42\t\n\x07request\"\xd0\x03\n\x0eInferenceStats\x12\x43\n\x1a\x65\x61rliest_client_created_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x88\x01\x01\x12\x41\n\x18latest_client_created_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01\x88\x01\x01\x12:\n\x12\x63lient_e2e_latency\x18\x03 \x01(\x0b\x32\x19.google.protobuf.DurationH\x02\x88\x01\x01\x12\x35\n\x12server_e2e_latency\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x39\n\x16preprocessing_duration\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x35\n\x12inference_duration\x18\x06 \x01(\x0b\x32\x19.google.protobuf.DurationB\x1d\n\x1b_earliest_client_created_atB\x1b\n\x19_latest_client_created_atB\x15\n\x13_client_e2e_latency\"Y\n\x14\x45otInferenceResponse\x12\x13\n\x0bprobability\x18\x01 \x01(\x02\x12,\n\x05stats\x18\x02 \x01(\x0b\x32\x1d.livekit.agent.InferenceStats\"}\n\x1dInterruptionInferenceResponse\x12\x17\n\x0fis_interruption\x18\x01 \x01(\x08\x12\x15\n\rprobabilities\x18\x02 \x03(\x02\x12,\n\x05stats\x18\x03 \x01(\x0b\x32\x1d.livekit.agent.InferenceStats\"\xbf\x01\n\x11InferenceResponse\x12\x45\n\x16\x65ot_inference_response\x18\x01 \x01(\x0b\x32#.livekit.agent.EotInferenceResponseH\x00\x12W\n\x1finterruption_inference_response\x18\x02 \x01(\x0b\x32,.livekit.agent.InterruptionInferenceResponseH\x00\x42\n\n\x08response\"\x8c\x03\n\x0eSessionCreated\x12P\n\x12\x64\x65\x66\x61ult_thresholds\x18\x01 \x03(\x0b\x32\x34.livekit.agent.SessionCreated.DefaultThresholdsEntry\x12\x19\n\x11\x64\x65\x66\x61ult_threshold\x18\x02 \x01(\x02\x12g\n\x1e\x64\x65\x66\x61ult_backchannel_thresholds\x18\x03 \x03(\x0b\x32?.livekit.agent.SessionCreated.DefaultBackchannelThresholdsEntry\x12%\n\x1d\x64\x65\x66\x61ult_backchannel_threshold\x18\x04 \x01(\x02\x1a\x38\n\x16\x44\x65\x66\x61ultThresholdsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01\x1a\x43\n!DefaultBackchannelThresholdsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x02:\x02\x38\x01\"\x12\n\x10InferenceStarted\"\x12\n\x10InferenceStopped\"\x0f\n\rSessionClosed\"\x90\x02\n\rEotPrediction\x12\x13\n\x0bprobability\x18\x01 \x01(\x02\x12\x36\n\x0finference_stats\x18\x02 \x01(\x0b\x32\x1d.livekit.agent.InferenceStats\x12\x38\n\x07\x62\x61\x63kend\x18\x03 \x01(\x0e\x32\'.livekit.agent.EotPrediction.EotBackend\x12\x1f\n\x17\x62\x61\x63kchannel_probability\x18\x04 \x01(\x02\"W\n\nEotBackend\x12\x17\n\x13\x45OT_BACKEND_UNKNOWN\x10\x00\x12\x1a\n\x16\x45OT_BACKEND_MULTIMODAL\x10\x01\x12\x14\n\x10\x45OT_BACKEND_TEXT\x10\x02\"\x80\x01\n\x16InterruptionPrediction\x12\x17\n\x0fis_interruption\x18\x01 \x01(\x08\x12\x15\n\rprobabilities\x18\x02 \x03(\x02\x12\x36\n\x0finference_stats\x18\x03 \x01(\x0b\x32\x1d.livekit.agent.InferenceStats\"\xeb\x04\n\rServerMessage\x12\x35\n\x11server_created_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\nrequest_id\x18\x02 \x01(\tH\x01\x88\x01\x01\x12:\n\x11\x63lient_created_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x02\x88\x01\x01\x12\x38\n\x0fsession_created\x18\x04 \x01(\x0b\x32\x1d.livekit.agent.SessionCreatedH\x00\x12<\n\x11inference_started\x18\x05 \x01(\x0b\x32\x1f.livekit.agent.InferenceStartedH\x00\x12<\n\x11inference_stopped\x18\x06 \x01(\x0b\x32\x1f.livekit.agent.InferenceStoppedH\x00\x12\x36\n\x0esession_closed\x18\x07 \x01(\x0b\x32\x1c.livekit.agent.SessionClosedH\x00\x12.\n\x05\x65rror\x18\x08 \x01(\x0b\x32\x1d.livekit.agent.InferenceErrorH\x00\x12\x36\n\x0e\x65ot_prediction\x18\t \x01(\x0b\x32\x1c.livekit.agent.EotPredictionH\x00\x12H\n\x17interruption_prediction\x18\n \x01(\x0b\x32%.livekit.agent.InterruptionPredictionH\x00\x42\t\n\x07messageB\r\n\x0b_request_idB\x14\n\x12_client_created_at*F\n\rAudioEncoding\x12\x1c\n\x18\x41UDIO_ENCODING_PCM_S16LE\x10\x00\x12\x17\n\x13\x41UDIO_ENCODING_OPUS\x10\x01\x42NH\x01Z)github.com/livekit/protocol/livekit/agent\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent.agent_inference', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'H\001Z)github.com/livekit/protocol/livekit/agent\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_SESSIONCREATED_DEFAULTTHRESHOLDSENTRY']._loaded_options = None + _globals['_SESSIONCREATED_DEFAULTTHRESHOLDSENTRY']._serialized_options = b'8\001' + _globals['_SESSIONCREATED_DEFAULTBACKCHANNELTHRESHOLDSENTRY']._loaded_options = None + _globals['_SESSIONCREATED_DEFAULTBACKCHANNELTHRESHOLDSENTRY']._serialized_options = b'8\001' + _globals['_AUDIOENCODING']._serialized_start=4479 + _globals['_AUDIOENCODING']._serialized_end=4549 + _globals['_SESSIONSETTINGS']._serialized_start=155 + _globals['_SESSIONSETTINGS']._serialized_end=380 + _globals['_INFERENCEERROR']._serialized_start=382 + _globals['_INFERENCEERROR']._serialized_end=429 + _globals['_EOTSETTINGS']._serialized_start=431 + _globals['_EOTSETTINGS']._serialized_end=499 + _globals['_INTERRUPTIONSETTINGS']._serialized_start=502 + _globals['_INTERRUPTIONSETTINGS']._serialized_end=731 + _globals['_SESSIONCREATE']._serialized_start=733 + _globals['_SESSIONCREATE']._serialized_end=798 + _globals['_INPUTAUDIO']._serialized_start=800 + _globals['_INPUTAUDIO']._serialized_end=896 + _globals['_EOTINPUTCHATCONTEXT']._serialized_start=898 + _globals['_EOTINPUTCHATCONTEXT']._serialized_end=965 + _globals['_SESSIONFLUSH']._serialized_start=967 + _globals['_SESSIONFLUSH']._serialized_end=981 + _globals['_SESSIONCLOSE']._serialized_start=983 + _globals['_SESSIONCLOSE']._serialized_end=997 + _globals['_INFERENCESTART']._serialized_start=999 + _globals['_INFERENCESTART']._serialized_end=1035 + _globals['_INFERENCESTOP']._serialized_start=1037 + _globals['_INFERENCESTOP']._serialized_end=1072 + _globals['_BUFFERSTART']._serialized_start=1074 + _globals['_BUFFERSTART']._serialized_end=1087 + _globals['_BUFFERSTOP']._serialized_start=1089 + _globals['_BUFFERSTOP']._serialized_end=1101 + _globals['_CLIENTMESSAGE']._serialized_start=1104 + _globals['_CLIENTMESSAGE']._serialized_end=1678 + _globals['_EOTINFERENCEREQUEST']._serialized_start=1681 + _globals['_EOTINFERENCEREQUEST']._serialized_end=1810 + _globals['_INTERRUPTIONINFERENCEREQUEST']._serialized_start=1812 + _globals['_INTERRUPTIONINFERENCEREQUEST']._serialized_end=1926 + _globals['_INFERENCEREQUEST']._serialized_start=1929 + _globals['_INFERENCEREQUEST']._serialized_end=2114 + _globals['_INFERENCESTATS']._serialized_start=2117 + _globals['_INFERENCESTATS']._serialized_end=2581 + _globals['_EOTINFERENCERESPONSE']._serialized_start=2583 + _globals['_EOTINFERENCERESPONSE']._serialized_end=2672 + _globals['_INTERRUPTIONINFERENCERESPONSE']._serialized_start=2674 + _globals['_INTERRUPTIONINFERENCERESPONSE']._serialized_end=2799 + _globals['_INFERENCERESPONSE']._serialized_start=2802 + _globals['_INFERENCERESPONSE']._serialized_end=2993 + _globals['_SESSIONCREATED']._serialized_start=2996 + _globals['_SESSIONCREATED']._serialized_end=3392 + _globals['_SESSIONCREATED_DEFAULTTHRESHOLDSENTRY']._serialized_start=3267 + _globals['_SESSIONCREATED_DEFAULTTHRESHOLDSENTRY']._serialized_end=3323 + _globals['_SESSIONCREATED_DEFAULTBACKCHANNELTHRESHOLDSENTRY']._serialized_start=3325 + _globals['_SESSIONCREATED_DEFAULTBACKCHANNELTHRESHOLDSENTRY']._serialized_end=3392 + _globals['_INFERENCESTARTED']._serialized_start=3394 + _globals['_INFERENCESTARTED']._serialized_end=3412 + _globals['_INFERENCESTOPPED']._serialized_start=3414 + _globals['_INFERENCESTOPPED']._serialized_end=3432 + _globals['_SESSIONCLOSED']._serialized_start=3434 + _globals['_SESSIONCLOSED']._serialized_end=3449 + _globals['_EOTPREDICTION']._serialized_start=3452 + _globals['_EOTPREDICTION']._serialized_end=3724 + _globals['_EOTPREDICTION_EOTBACKEND']._serialized_start=3637 + _globals['_EOTPREDICTION_EOTBACKEND']._serialized_end=3724 + _globals['_INTERRUPTIONPREDICTION']._serialized_start=3727 + _globals['_INTERRUPTIONPREDICTION']._serialized_end=3855 + _globals['_SERVERMESSAGE']._serialized_start=3858 + _globals['_SERVERMESSAGE']._serialized_end=4477 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_inference.pyi b/livekit-protocol/livekit/protocol/agent_pb/agent_inference.pyi new file mode 100644 index 00000000..b61d4f55 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_inference.pyi @@ -0,0 +1,295 @@ +from . import agent_session as _agent_session +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class AudioEncoding(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AUDIO_ENCODING_PCM_S16LE: _ClassVar[AudioEncoding] + AUDIO_ENCODING_OPUS: _ClassVar[AudioEncoding] +AUDIO_ENCODING_PCM_S16LE: AudioEncoding +AUDIO_ENCODING_OPUS: AudioEncoding + +class SessionSettings(_message.Message): + __slots__ = ("sample_rate", "encoding", "eot_settings", "interruption_settings") + SAMPLE_RATE_FIELD_NUMBER: _ClassVar[int] + ENCODING_FIELD_NUMBER: _ClassVar[int] + EOT_SETTINGS_FIELD_NUMBER: _ClassVar[int] + INTERRUPTION_SETTINGS_FIELD_NUMBER: _ClassVar[int] + sample_rate: int + encoding: AudioEncoding + eot_settings: EotSettings + interruption_settings: InterruptionSettings + def __init__(self, sample_rate: _Optional[int] = ..., encoding: _Optional[_Union[AudioEncoding, str]] = ..., eot_settings: _Optional[_Union[EotSettings, _Mapping]] = ..., interruption_settings: _Optional[_Union[InterruptionSettings, _Mapping]] = ...) -> None: ... + +class InferenceError(_message.Message): + __slots__ = ("message", "code") + MESSAGE_FIELD_NUMBER: _ClassVar[int] + CODE_FIELD_NUMBER: _ClassVar[int] + message: str + code: int + def __init__(self, message: _Optional[str] = ..., code: _Optional[int] = ...) -> None: ... + +class EotSettings(_message.Message): + __slots__ = ("detection_interval",) + DETECTION_INTERVAL_FIELD_NUMBER: _ClassVar[int] + detection_interval: _duration_pb2.Duration + def __init__(self, detection_interval: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class InterruptionSettings(_message.Message): + __slots__ = ("threshold", "min_frames", "max_audio_duration", "audio_prefix_duration", "detection_interval") + THRESHOLD_FIELD_NUMBER: _ClassVar[int] + MIN_FRAMES_FIELD_NUMBER: _ClassVar[int] + MAX_AUDIO_DURATION_FIELD_NUMBER: _ClassVar[int] + AUDIO_PREFIX_DURATION_FIELD_NUMBER: _ClassVar[int] + DETECTION_INTERVAL_FIELD_NUMBER: _ClassVar[int] + threshold: float + min_frames: int + max_audio_duration: _duration_pb2.Duration + audio_prefix_duration: _duration_pb2.Duration + detection_interval: _duration_pb2.Duration + def __init__(self, threshold: _Optional[float] = ..., min_frames: _Optional[int] = ..., max_audio_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., audio_prefix_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., detection_interval: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class SessionCreate(_message.Message): + __slots__ = ("settings",) + SETTINGS_FIELD_NUMBER: _ClassVar[int] + settings: SessionSettings + def __init__(self, settings: _Optional[_Union[SessionSettings, _Mapping]] = ...) -> None: ... + +class InputAudio(_message.Message): + __slots__ = ("audio", "created_at", "num_samples") + AUDIO_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + NUM_SAMPLES_FIELD_NUMBER: _ClassVar[int] + audio: bytes + created_at: _timestamp_pb2.Timestamp + num_samples: int + def __init__(self, audio: _Optional[bytes] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., num_samples: _Optional[int] = ...) -> None: ... + +class EotInputChatContext(_message.Message): + __slots__ = ("messages",) + MESSAGES_FIELD_NUMBER: _ClassVar[int] + messages: _containers.RepeatedCompositeFieldContainer[_agent_session.ChatMessage] + def __init__(self, messages: _Optional[_Iterable[_Union[_agent_session.ChatMessage, _Mapping]]] = ...) -> None: ... + +class SessionFlush(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class SessionClose(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class InferenceStart(_message.Message): + __slots__ = ("request_id",) + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + request_id: str + def __init__(self, request_id: _Optional[str] = ...) -> None: ... + +class InferenceStop(_message.Message): + __slots__ = ("request_id",) + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + request_id: str + def __init__(self, request_id: _Optional[str] = ...) -> None: ... + +class BufferStart(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class BufferStop(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ClientMessage(_message.Message): + __slots__ = ("created_at", "session_create", "input_audio", "session_flush", "session_close", "inference_start", "inference_stop", "buffer_start", "buffer_stop", "eot_input_chat_context") + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + SESSION_CREATE_FIELD_NUMBER: _ClassVar[int] + INPUT_AUDIO_FIELD_NUMBER: _ClassVar[int] + SESSION_FLUSH_FIELD_NUMBER: _ClassVar[int] + SESSION_CLOSE_FIELD_NUMBER: _ClassVar[int] + INFERENCE_START_FIELD_NUMBER: _ClassVar[int] + INFERENCE_STOP_FIELD_NUMBER: _ClassVar[int] + BUFFER_START_FIELD_NUMBER: _ClassVar[int] + BUFFER_STOP_FIELD_NUMBER: _ClassVar[int] + EOT_INPUT_CHAT_CONTEXT_FIELD_NUMBER: _ClassVar[int] + created_at: _timestamp_pb2.Timestamp + session_create: SessionCreate + input_audio: InputAudio + session_flush: SessionFlush + session_close: SessionClose + inference_start: InferenceStart + inference_stop: InferenceStop + buffer_start: BufferStart + buffer_stop: BufferStop + eot_input_chat_context: EotInputChatContext + def __init__(self, created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., session_create: _Optional[_Union[SessionCreate, _Mapping]] = ..., input_audio: _Optional[_Union[InputAudio, _Mapping]] = ..., session_flush: _Optional[_Union[SessionFlush, _Mapping]] = ..., session_close: _Optional[_Union[SessionClose, _Mapping]] = ..., inference_start: _Optional[_Union[InferenceStart, _Mapping]] = ..., inference_stop: _Optional[_Union[InferenceStop, _Mapping]] = ..., buffer_start: _Optional[_Union[BufferStart, _Mapping]] = ..., buffer_stop: _Optional[_Union[BufferStop, _Mapping]] = ..., eot_input_chat_context: _Optional[_Union[EotInputChatContext, _Mapping]] = ...) -> None: ... + +class EotInferenceRequest(_message.Message): + __slots__ = ("audio", "assistant_text", "encoding", "sample_rate") + AUDIO_FIELD_NUMBER: _ClassVar[int] + ASSISTANT_TEXT_FIELD_NUMBER: _ClassVar[int] + ENCODING_FIELD_NUMBER: _ClassVar[int] + SAMPLE_RATE_FIELD_NUMBER: _ClassVar[int] + audio: bytes + assistant_text: str + encoding: AudioEncoding + sample_rate: int + def __init__(self, audio: _Optional[bytes] = ..., assistant_text: _Optional[str] = ..., encoding: _Optional[_Union[AudioEncoding, str]] = ..., sample_rate: _Optional[int] = ...) -> None: ... + +class InterruptionInferenceRequest(_message.Message): + __slots__ = ("audio", "encoding", "sample_rate") + AUDIO_FIELD_NUMBER: _ClassVar[int] + ENCODING_FIELD_NUMBER: _ClassVar[int] + SAMPLE_RATE_FIELD_NUMBER: _ClassVar[int] + audio: bytes + encoding: AudioEncoding + sample_rate: int + def __init__(self, audio: _Optional[bytes] = ..., encoding: _Optional[_Union[AudioEncoding, str]] = ..., sample_rate: _Optional[int] = ...) -> None: ... + +class InferenceRequest(_message.Message): + __slots__ = ("eot_inference_request", "interruption_inference_request") + EOT_INFERENCE_REQUEST_FIELD_NUMBER: _ClassVar[int] + INTERRUPTION_INFERENCE_REQUEST_FIELD_NUMBER: _ClassVar[int] + eot_inference_request: EotInferenceRequest + interruption_inference_request: InterruptionInferenceRequest + def __init__(self, eot_inference_request: _Optional[_Union[EotInferenceRequest, _Mapping]] = ..., interruption_inference_request: _Optional[_Union[InterruptionInferenceRequest, _Mapping]] = ...) -> None: ... + +class InferenceStats(_message.Message): + __slots__ = ("earliest_client_created_at", "latest_client_created_at", "client_e2e_latency", "server_e2e_latency", "preprocessing_duration", "inference_duration") + EARLIEST_CLIENT_CREATED_AT_FIELD_NUMBER: _ClassVar[int] + LATEST_CLIENT_CREATED_AT_FIELD_NUMBER: _ClassVar[int] + CLIENT_E2E_LATENCY_FIELD_NUMBER: _ClassVar[int] + SERVER_E2E_LATENCY_FIELD_NUMBER: _ClassVar[int] + PREPROCESSING_DURATION_FIELD_NUMBER: _ClassVar[int] + INFERENCE_DURATION_FIELD_NUMBER: _ClassVar[int] + earliest_client_created_at: _timestamp_pb2.Timestamp + latest_client_created_at: _timestamp_pb2.Timestamp + client_e2e_latency: _duration_pb2.Duration + server_e2e_latency: _duration_pb2.Duration + preprocessing_duration: _duration_pb2.Duration + inference_duration: _duration_pb2.Duration + def __init__(self, earliest_client_created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., latest_client_created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., client_e2e_latency: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., server_e2e_latency: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., preprocessing_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., inference_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class EotInferenceResponse(_message.Message): + __slots__ = ("probability", "stats") + PROBABILITY_FIELD_NUMBER: _ClassVar[int] + STATS_FIELD_NUMBER: _ClassVar[int] + probability: float + stats: InferenceStats + def __init__(self, probability: _Optional[float] = ..., stats: _Optional[_Union[InferenceStats, _Mapping]] = ...) -> None: ... + +class InterruptionInferenceResponse(_message.Message): + __slots__ = ("is_interruption", "probabilities", "stats") + IS_INTERRUPTION_FIELD_NUMBER: _ClassVar[int] + PROBABILITIES_FIELD_NUMBER: _ClassVar[int] + STATS_FIELD_NUMBER: _ClassVar[int] + is_interruption: bool + probabilities: _containers.RepeatedScalarFieldContainer[float] + stats: InferenceStats + def __init__(self, is_interruption: bool = ..., probabilities: _Optional[_Iterable[float]] = ..., stats: _Optional[_Union[InferenceStats, _Mapping]] = ...) -> None: ... + +class InferenceResponse(_message.Message): + __slots__ = ("eot_inference_response", "interruption_inference_response") + EOT_INFERENCE_RESPONSE_FIELD_NUMBER: _ClassVar[int] + INTERRUPTION_INFERENCE_RESPONSE_FIELD_NUMBER: _ClassVar[int] + eot_inference_response: EotInferenceResponse + interruption_inference_response: InterruptionInferenceResponse + def __init__(self, eot_inference_response: _Optional[_Union[EotInferenceResponse, _Mapping]] = ..., interruption_inference_response: _Optional[_Union[InterruptionInferenceResponse, _Mapping]] = ...) -> None: ... + +class SessionCreated(_message.Message): + __slots__ = ("default_thresholds", "default_threshold", "default_backchannel_thresholds", "default_backchannel_threshold") + class DefaultThresholdsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: float + def __init__(self, key: _Optional[str] = ..., value: _Optional[float] = ...) -> None: ... + class DefaultBackchannelThresholdsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: float + def __init__(self, key: _Optional[str] = ..., value: _Optional[float] = ...) -> None: ... + DEFAULT_THRESHOLDS_FIELD_NUMBER: _ClassVar[int] + DEFAULT_THRESHOLD_FIELD_NUMBER: _ClassVar[int] + DEFAULT_BACKCHANNEL_THRESHOLDS_FIELD_NUMBER: _ClassVar[int] + DEFAULT_BACKCHANNEL_THRESHOLD_FIELD_NUMBER: _ClassVar[int] + default_thresholds: _containers.ScalarMap[str, float] + default_threshold: float + default_backchannel_thresholds: _containers.ScalarMap[str, float] + default_backchannel_threshold: float + def __init__(self, default_thresholds: _Optional[_Mapping[str, float]] = ..., default_threshold: _Optional[float] = ..., default_backchannel_thresholds: _Optional[_Mapping[str, float]] = ..., default_backchannel_threshold: _Optional[float] = ...) -> None: ... + +class InferenceStarted(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class InferenceStopped(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class SessionClosed(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class EotPrediction(_message.Message): + __slots__ = ("probability", "inference_stats", "backend", "backchannel_probability") + class EotBackend(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + EOT_BACKEND_UNKNOWN: _ClassVar[EotPrediction.EotBackend] + EOT_BACKEND_MULTIMODAL: _ClassVar[EotPrediction.EotBackend] + EOT_BACKEND_TEXT: _ClassVar[EotPrediction.EotBackend] + EOT_BACKEND_UNKNOWN: EotPrediction.EotBackend + EOT_BACKEND_MULTIMODAL: EotPrediction.EotBackend + EOT_BACKEND_TEXT: EotPrediction.EotBackend + PROBABILITY_FIELD_NUMBER: _ClassVar[int] + INFERENCE_STATS_FIELD_NUMBER: _ClassVar[int] + BACKEND_FIELD_NUMBER: _ClassVar[int] + BACKCHANNEL_PROBABILITY_FIELD_NUMBER: _ClassVar[int] + probability: float + inference_stats: InferenceStats + backend: EotPrediction.EotBackend + backchannel_probability: float + def __init__(self, probability: _Optional[float] = ..., inference_stats: _Optional[_Union[InferenceStats, _Mapping]] = ..., backend: _Optional[_Union[EotPrediction.EotBackend, str]] = ..., backchannel_probability: _Optional[float] = ...) -> None: ... + +class InterruptionPrediction(_message.Message): + __slots__ = ("is_interruption", "probabilities", "inference_stats") + IS_INTERRUPTION_FIELD_NUMBER: _ClassVar[int] + PROBABILITIES_FIELD_NUMBER: _ClassVar[int] + INFERENCE_STATS_FIELD_NUMBER: _ClassVar[int] + is_interruption: bool + probabilities: _containers.RepeatedScalarFieldContainer[float] + inference_stats: InferenceStats + def __init__(self, is_interruption: bool = ..., probabilities: _Optional[_Iterable[float]] = ..., inference_stats: _Optional[_Union[InferenceStats, _Mapping]] = ...) -> None: ... + +class ServerMessage(_message.Message): + __slots__ = ("server_created_at", "request_id", "client_created_at", "session_created", "inference_started", "inference_stopped", "session_closed", "error", "eot_prediction", "interruption_prediction") + SERVER_CREATED_AT_FIELD_NUMBER: _ClassVar[int] + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + CLIENT_CREATED_AT_FIELD_NUMBER: _ClassVar[int] + SESSION_CREATED_FIELD_NUMBER: _ClassVar[int] + INFERENCE_STARTED_FIELD_NUMBER: _ClassVar[int] + INFERENCE_STOPPED_FIELD_NUMBER: _ClassVar[int] + SESSION_CLOSED_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + EOT_PREDICTION_FIELD_NUMBER: _ClassVar[int] + INTERRUPTION_PREDICTION_FIELD_NUMBER: _ClassVar[int] + server_created_at: _timestamp_pb2.Timestamp + request_id: str + client_created_at: _timestamp_pb2.Timestamp + session_created: SessionCreated + inference_started: InferenceStarted + inference_stopped: InferenceStopped + session_closed: SessionClosed + error: InferenceError + eot_prediction: EotPrediction + interruption_prediction: InterruptionPrediction + def __init__(self, server_created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., request_id: _Optional[str] = ..., client_created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., session_created: _Optional[_Union[SessionCreated, _Mapping]] = ..., inference_started: _Optional[_Union[InferenceStarted, _Mapping]] = ..., inference_stopped: _Optional[_Union[InferenceStopped, _Mapping]] = ..., session_closed: _Optional[_Union[SessionClosed, _Mapping]] = ..., error: _Optional[_Union[InferenceError, _Mapping]] = ..., eot_prediction: _Optional[_Union[EotPrediction, _Mapping]] = ..., interruption_prediction: _Optional[_Union[InterruptionPrediction, _Mapping]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_session.py b/livekit-protocol/livekit/protocol/agent_pb/agent_session.py new file mode 100644 index 00000000..fb78ab9e --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_session.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agent/livekit_agent_session.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'agent/livekit_agent_session.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from ..logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!agent/livekit_agent_session.proto\x12\rlivekit.agent\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x14logger/options.proto\"\x9d\x04\n\rMetricsReport\x12\x37\n\x13started_speaking_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x13stopped_speaking_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12 \n\x13transcription_delay\x18\x03 \x01(\x01H\x00\x88\x01\x01\x12\x1e\n\x11\x65nd_of_turn_delay\x18\x04 \x01(\x01H\x01\x88\x01\x01\x12)\n\x1con_user_turn_completed_delay\x18\x05 \x01(\x01H\x02\x88\x01\x01\x12\x1a\n\rllm_node_ttft\x18\x06 \x01(\x01H\x03\x88\x01\x01\x12\x1a\n\rtts_node_ttfb\x18\x07 \x01(\x01H\x04\x88\x01\x01\x12\x18\n\x0b\x65\x32\x65_latency\x18\x08 \x01(\x01H\x05\x88\x01\x01\x12\x19\n\x0cllm_node_tps\x18\t \x01(\x01H\x06\x88\x01\x01\x12\x1a\n\rllm_node_ttfs\x18\n \x01(\x01H\x07\x88\x01\x01\x42\x16\n\x14_transcription_delayB\x14\n\x12_end_of_turn_delayB\x1f\n\x1d_on_user_turn_completed_delayB\x10\n\x0e_llm_node_ttftB\x10\n\x0e_tts_node_ttfbB\x0e\n\x0c_e2e_latencyB\x0f\n\r_llm_node_tpsB\x10\n\x0e_llm_node_ttfs\"\xed\x01\n\x0bTimedString\x12\x0c\n\x04text\x18\x01 \x01(\t\x12\x17\n\nstart_time\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\x03 \x01(\x01H\x01\x88\x01\x01\x12\x17\n\nconfidence\x18\x04 \x01(\x01H\x02\x88\x01\x01\x12\x1e\n\x11start_time_offset\x18\x05 \x01(\x01H\x03\x88\x01\x01\x12\x17\n\nspeaker_id\x18\x06 \x01(\tH\x04\x88\x01\x01\x42\r\n\x0b_start_timeB\x0b\n\t_end_timeB\r\n\x0b_confidenceB\x14\n\x12_start_time_offsetB\r\n\x0b_speaker_id\"\xb9\x03\n\x0b\x43hatMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12%\n\x04role\x18\x02 \x01(\x0e\x32\x17.livekit.agent.ChatRole\x12\x37\n\x07\x63ontent\x18\x03 \x03(\x0b\x32&.livekit.agent.ChatMessage.ChatContent\x12\x13\n\x0binterrupted\x18\x04 \x01(\x08\x12\"\n\x15transcript_confidence\x18\x05 \x01(\x01H\x00\x88\x01\x01\x12\x34\n\x05\x65xtra\x18\x06 \x03(\x0b\x32%.livekit.agent.ChatMessage.ExtraEntry\x12-\n\x07metrics\x18\x07 \x01(\x0b\x32\x1c.livekit.agent.MetricsReport\x12.\n\ncreated_at\x18\x08 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a(\n\x0b\x43hatContent\x12\x0e\n\x04text\x18\x01 \x01(\tH\x00\x42\t\n\x07payload\x1a,\n\nExtraEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x18\n\x16_transcript_confidence\"\x87\x01\n\x0c\x46unctionCall\x12\n\n\x02id\x18\x01 \x01(\t\x12\x1a\n\x07\x63\x61ll_id\x18\x02 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x11\n\targuments\x18\x03 \x01(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12.\n\ncreated_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\x9c\x01\n\x12\x46unctionCallOutput\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x1a\n\x07\x63\x61ll_id\x18\x03 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x0e\n\x06output\x18\x04 \x01(\t\x12\x10\n\x08is_error\x18\x05 \x01(\x08\x12.\n\ncreated_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xaa\x01\n\x0c\x41gentHandoff\x12\n\n\x02id\x18\x01 \x01(\t\x12(\n\x0cold_agent_id\x18\x02 \x01(\tB\r\xbaP\noldAgentIDH\x00\x88\x01\x01\x12#\n\x0cnew_agent_id\x18\x03 \x01(\tB\r\xbaP\nnewAgentID\x12.\n\ncreated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0f\n\r_old_agent_id\"\xa7\x01\n\x11\x41gentConfigUpdate\x12\n\n\x02id\x18\x01 \x01(\t\x12\x19\n\x0cinstructions\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x13\n\x0btools_added\x18\x03 \x03(\t\x12\x15\n\rtools_removed\x18\x04 \x03(\t\x12.\n\ncreated_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x0f\n\r_instructions\"\xf5\x02\n\x0b\x43hatContext\x12\x32\n\x05items\x18\x01 \x03(\x0b\x32#.livekit.agent.ChatContext.ChatItem\x1a\xb1\x02\n\x08\x43hatItem\x12-\n\x07message\x18\x01 \x01(\x0b\x32\x1a.livekit.agent.ChatMessageH\x00\x12\x34\n\rfunction_call\x18\x02 \x01(\x0b\x32\x1b.livekit.agent.FunctionCallH\x00\x12\x41\n\x14\x66unction_call_output\x18\x03 \x01(\x0b\x32!.livekit.agent.FunctionCallOutputH\x00\x12\x34\n\ragent_handoff\x18\x04 \x01(\x0b\x32\x1b.livekit.agent.AgentHandoffH\x00\x12?\n\x13\x61gent_config_update\x18\x05 \x01(\x0b\x32 .livekit.agent.AgentConfigUpdateH\x00\x42\x06\n\x04item\"\x88\x03\n\rLLMModelUsage\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x14\n\x0cinput_tokens\x18\x03 \x01(\x05\x12\x1b\n\x13input_cached_tokens\x18\x04 \x01(\x05\x12\x1a\n\x12input_audio_tokens\x18\x05 \x01(\x05\x12!\n\x19input_cached_audio_tokens\x18\x06 \x01(\x05\x12\x19\n\x11input_text_tokens\x18\x07 \x01(\x05\x12 \n\x18input_cached_text_tokens\x18\x08 \x01(\x05\x12\x1a\n\x12input_image_tokens\x18\t \x01(\x05\x12!\n\x19input_cached_image_tokens\x18\n \x01(\x05\x12\x15\n\routput_tokens\x18\x0b \x01(\x05\x12\x1b\n\x13output_audio_tokens\x18\x0c \x01(\x05\x12\x1a\n\x12output_text_tokens\x18\r \x01(\x05\x12\x18\n\x10session_duration\x18\x0e \x01(\x01\"\x8f\x01\n\rTTSModelUsage\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x14\n\x0cinput_tokens\x18\x03 \x01(\x05\x12\x15\n\routput_tokens\x18\x04 \x01(\x05\x12\x18\n\x10\x63haracters_count\x18\x05 \x01(\x05\x12\x16\n\x0e\x61udio_duration\x18\x06 \x01(\x01\"u\n\rSTTModelUsage\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x14\n\x0cinput_tokens\x18\x03 \x01(\x05\x12\x15\n\routput_tokens\x18\x04 \x01(\x05\x12\x16\n\x0e\x61udio_duration\x18\x05 \x01(\x01\"Q\n\x16InterruptionModelUsage\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x16\n\x0etotal_requests\x18\x03 \x01(\x05\"H\n\rEotModelUsage\x12\x10\n\x08provider\x18\x01 \x01(\t\x12\r\n\x05model\x18\x02 \x01(\t\x12\x16\n\x0etotal_requests\x18\x03 \x01(\x05\"\x88\x02\n\nModelUsage\x12+\n\x03llm\x18\x01 \x01(\x0b\x32\x1c.livekit.agent.LLMModelUsageH\x00\x12+\n\x03tts\x18\x02 \x01(\x0b\x32\x1c.livekit.agent.TTSModelUsageH\x00\x12+\n\x03stt\x18\x03 \x01(\x0b\x32\x1c.livekit.agent.STTModelUsageH\x00\x12=\n\x0cinterruption\x18\x04 \x01(\x0b\x32%.livekit.agent.InterruptionModelUsageH\x00\x12+\n\x03\x65ot\x18\x05 \x01(\x0b\x32\x1c.livekit.agent.EotModelUsageH\x00\x42\x07\n\x05usage\"C\n\x11\x41gentSessionUsage\x12.\n\x0bmodel_usage\x18\x01 \x03(\x0b\x32\x19.livekit.agent.ModelUsage\"8\n\x0c\x44\x65\x62ugMessage\x12(\n\x07payload\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\"\xf2\x19\n\x11\x41gentSessionEvent\x12.\n\ncreated_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12Q\n\x13\x61gent_state_changed\x18\n \x01(\x0b\x32\x32.livekit.agent.AgentSessionEvent.AgentStateChangedH\x00\x12O\n\x12user_state_changed\x18\x0b \x01(\x0b\x32\x31.livekit.agent.AgentSessionEvent.UserStateChangedH\x00\x12Y\n\x17\x63onversation_item_added\x18\x0c \x01(\x0b\x32\x36.livekit.agent.AgentSessionEvent.ConversationItemAddedH\x00\x12W\n\x16user_input_transcribed\x18\r \x01(\x0b\x32\x35.livekit.agent.AgentSessionEvent.UserInputTranscribedH\x00\x12Y\n\x17\x66unction_tools_executed\x18\x0e \x01(\x0b\x32\x36.livekit.agent.AgentSessionEvent.FunctionToolsExecutedH\x00\x12\x37\n\x05\x65rror\x18\x0f \x01(\x0b\x32&.livekit.agent.AgentSessionEvent.ErrorH\x00\x12P\n\x12overlapping_speech\x18\x10 \x01(\x0b\x32\x32.livekit.agent.AgentSessionEvent.OverlappingSpeechH\x00\x12U\n\x15session_usage_updated\x18\x11 \x01(\x0b\x32\x34.livekit.agent.AgentSessionEvent.SessionUsageUpdatedH\x00\x12H\n\x0e\x61md_prediction\x18\x12 \x01(\x0b\x32..livekit.agent.AgentSessionEvent.AmdPredictionH\x00\x12H\n\x0e\x65ot_prediction\x18\x13 \x01(\x0b\x32..livekit.agent.AgentSessionEvent.EotPredictionH\x00\x12W\n\x16\x66unction_tools_started\x18\x14 \x01(\x0b\x32\x35.livekit.agent.AgentSessionEvent.FunctionToolsStartedH\x00\x12\x34\n\rdebug_message\x18\x15 \x01(\x0b\x32\x1b.livekit.agent.DebugMessageH\x00\x12W\n\x16tool_execution_updated\x18\x16 \x01(\x0b\x32\x35.livekit.agent.AgentSessionEvent.ToolExecutionUpdatedH\x00\x12[\n\x18\x61gent_false_interruption\x18\x17 \x01(\x0b\x32\x37.livekit.agent.AgentSessionEvent.AgentFalseInterruptionH\x00\x1ao\n\x11\x41gentStateChanged\x12,\n\told_state\x18\x01 \x01(\x0e\x32\x19.livekit.agent.AgentState\x12,\n\tnew_state\x18\x02 \x01(\x0e\x32\x19.livekit.agent.AgentState\x1al\n\x10UserStateChanged\x12+\n\told_state\x18\x01 \x01(\x0e\x32\x18.livekit.agent.UserState\x12+\n\tnew_state\x18\x02 \x01(\x0e\x32\x18.livekit.agent.UserState\x1aJ\n\x15\x43onversationItemAdded\x12\x31\n\x04item\x18\x01 \x01(\x0b\x32#.livekit.agent.ChatContext.ChatItem\x1a`\n\x14UserInputTranscribed\x12\x12\n\ntranscript\x18\x01 \x01(\t\x12\x10\n\x08is_final\x18\x02 \x01(\x08\x12\x15\n\x08language\x18\x03 \x01(\tH\x00\x88\x01\x01\x42\x0b\n\t_language\x1aK\n\x14\x46unctionToolsStarted\x12\x33\n\x0e\x66unction_calls\x18\x01 \x03(\x0b\x32\x1b.livekit.agent.FunctionCall\x1a\x8e\x01\n\x15\x46unctionToolsExecuted\x12\x33\n\x0e\x66unction_calls\x18\x01 \x03(\x0b\x32\x1b.livekit.agent.FunctionCall\x12@\n\x15\x66unction_call_outputs\x18\x02 \x03(\x0b\x32!.livekit.agent.FunctionCallOutput\x1a\x18\n\x05\x45rror\x12\x0f\n\x07message\x18\x01 \x01(\t\x1a\xca\x01\n\x11OverlappingSpeech\x12\x17\n\x0fis_interruption\x18\x01 \x01(\x08\x12;\n\x12overlap_started_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x00\x88\x01\x01\x12\x17\n\x0f\x64\x65tection_delay\x18\x03 \x01(\x01\x12/\n\x0b\x64\x65tected_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.TimestampB\x15\n\x13_overlap_started_at\x1a\xbf\x01\n\rAmdPrediction\x12\x32\n\x0fspeech_duration\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12,\n\x08\x63\x61tegory\x18\x02 \x01(\x0e\x32\x1a.livekit.agent.AmdCategory\x12\x0e\n\x06reason\x18\x03 \x01(\t\x12\x12\n\ntranscript\x18\x04 \x01(\t\x12(\n\x05\x64\x65lay\x18\x05 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x98\x01\n\rEotPrediction\x12\x13\n\x0bprobability\x18\x01 \x01(\x02\x12\x11\n\tthreshold\x18\x02 \x01(\x02\x12\x35\n\x12inference_duration\x18\x03 \x01(\x0b\x32\x19.google.protobuf.Duration\x12(\n\x05\x64\x65lay\x18\x04 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a\x46\n\x13SessionUsageUpdated\x12/\n\x05usage\x18\x01 \x01(\x0b\x32 .livekit.agent.AgentSessionUsage\x1a\x93\x06\n\x14ToolExecutionUpdated\x12P\n\x07started\x18\x01 \x01(\x0b\x32=.livekit.agent.AgentSessionEvent.ToolExecutionUpdated.StartedH\x00\x12Y\n\x0c\x63\x61ll_updated\x18\x02 \x01(\x0b\x32\x41.livekit.agent.AgentSessionEvent.ToolExecutionUpdated.CallUpdatedH\x00\x12[\n\rreply_updated\x18\x03 \x01(\x0b\x32\x42.livekit.agent.AgentSessionEvent.ToolExecutionUpdated.ReplyUpdatedH\x00\x12L\n\x05\x65nded\x18\x04 \x01(\x0b\x32;.livekit.agent.AgentSessionEvent.ToolExecutionUpdated.EndedH\x00\x1a=\n\x07Started\x12\x32\n\rfunction_call\x18\x01 \x01(\x0b\x32\x1b.livekit.agent.FunctionCall\x1aM\n\x0b\x43\x61llUpdated\x12\x11\n\x02id\x18\x01 \x01(\tB\x05\xbaP\x02ID\x12\x1a\n\x07\x63\x61ll_id\x18\x02 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x0f\n\x07message\x18\x03 \x01(\t\x1a\x87\x01\n\x05\x45nded\x12\x11\n\x02id\x18\x01 \x01(\tB\x05\xbaP\x02ID\x12\x1a\n\x07\x63\x61ll_id\x18\x02 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x14\n\x07message\x18\x03 \x01(\tH\x00\x88\x01\x01\x12-\n\x06status\x18\x04 \x01(\x0e\x32\x1d.livekit.agent.ToolCallStatusB\n\n\x08_message\x1a\x80\x01\n\x0cReplyUpdated\x12 \n\nupdate_ids\x18\x01 \x03(\tB\x0c\xbaP\tupdateIDs\x12.\n\x06status\x18\x02 \x01(\x0e\x32\x1e.livekit.agent.ToolReplyStatus\x12\x1e\n\tspeech_id\x18\x03 \x01(\tB\x0b\xbaP\x08speechIDB\x08\n\x06update\x1a)\n\x16\x41gentFalseInterruption\x12\x0f\n\x07resumed\x18\x01 \x01(\x08\x42\x07\n\x05\x65vent\"\xf6\n\n\x0eSessionRequest\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x32\n\x04ping\x18\x02 \x01(\x0b\x32\".livekit.agent.SessionRequest.PingH\x00\x12H\n\x10get_chat_history\x18\x04 \x01(\x0b\x32,.livekit.agent.SessionRequest.GetChatHistoryH\x00\x12;\n\trun_input\x18\x05 \x01(\x0b\x32&.livekit.agent.SessionRequest.RunInputH\x00\x12\x44\n\x0eget_agent_info\x18\x06 \x01(\x0b\x32*.livekit.agent.SessionRequest.GetAgentInfoH\x00\x12J\n\x11get_session_state\x18\x07 \x01(\x0b\x32-.livekit.agent.SessionRequest.GetSessionStateH\x00\x12\x42\n\rget_rtc_stats\x18\x08 \x01(\x0b\x32).livekit.agent.SessionRequest.GetRTCStatsH\x00\x12J\n\x11get_session_usage\x18\t \x01(\x0b\x32-.livekit.agent.SessionRequest.GetSessionUsageH\x00\x12L\n\x12get_framework_info\x18\n \x01(\x0b\x32..livekit.agent.SessionRequest.GetFrameworkInfoH\x00\x12;\n\tupdate_io\x18\x0b \x01(\x0b\x32&.livekit.agent.SessionRequest.UpdateIOH\x00\x12O\n\x13\x66inalize_simulation\x18\x0c \x01(\x0b\x32\x30.livekit.agent.SessionRequest.FinalizeSimulationH\x00\x1a\x06\n\x04Ping\x1a\x10\n\x0eGetChatHistory\x1a\x18\n\x08RunInput\x12\x0c\n\x04text\x18\x01 \x01(\t\x1a\x0e\n\x0cGetAgentInfo\x1a\x11\n\x0fGetSessionState\x1a\r\n\x0bGetRTCStats\x1a\x11\n\x0fGetSessionUsage\x1a\x12\n\x10GetFrameworkInfo\x1a\xaf\x03\n\x08UpdateIO\x12@\n\x05input\x18\x01 \x01(\x0b\x32,.livekit.agent.SessionRequest.UpdateIO.InputH\x00\x88\x01\x01\x12\x42\n\x06output\x18\x02 \x01(\x0b\x32-.livekit.agent.SessionRequest.UpdateIO.OutputH\x01\x88\x01\x01\x1a\x63\n\x05Input\x12\x1a\n\raudio_enabled\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rvideo_enabled\x18\x02 \x01(\x08H\x01\x88\x01\x01\x42\x10\n\x0e_audio_enabledB\x10\n\x0e_video_enabled\x1a\xa2\x01\n\x06Output\x12\x1a\n\raudio_enabled\x18\x01 \x01(\x08H\x00\x88\x01\x01\x12\x1a\n\rvideo_enabled\x18\x02 \x01(\x08H\x01\x88\x01\x01\x12\"\n\x15transcription_enabled\x18\x03 \x01(\x08H\x02\x88\x01\x01\x42\x10\n\x0e_audio_enabledB\x10\n\x0e_video_enabledB\x18\n\x16_transcription_enabledB\x08\n\x06_inputB\t\n\x07_output\x1aM\n\x12\x46inalizeSimulation\x12\x1b\n\x13provisional_success\x18\x01 \x01(\x08\x12\x1a\n\x12provisional_reason\x18\x02 \x01(\tB\t\n\x07request\"\xd3\x0f\n\x0fSessionResponse\x12\x12\n\nrequest_id\x18\x01 \x01(\t\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x01\x88\x01\x01\x12\x33\n\x04pong\x18\x03 \x01(\x0b\x32#.livekit.agent.SessionResponse.PongH\x00\x12Q\n\x10get_chat_history\x18\x05 \x01(\x0b\x32\x35.livekit.agent.SessionResponse.GetChatHistoryResponseH\x00\x12\x44\n\trun_input\x18\x06 \x01(\x0b\x32/.livekit.agent.SessionResponse.RunInputResponseH\x00\x12M\n\x0eget_agent_info\x18\x07 \x01(\x0b\x32\x33.livekit.agent.SessionResponse.GetAgentInfoResponseH\x00\x12S\n\x11get_session_state\x18\x08 \x01(\x0b\x32\x36.livekit.agent.SessionResponse.GetSessionStateResponseH\x00\x12K\n\rget_rtc_stats\x18\t \x01(\x0b\x32\x32.livekit.agent.SessionResponse.GetRTCStatsResponseH\x00\x12S\n\x11get_session_usage\x18\n \x01(\x0b\x32\x36.livekit.agent.SessionResponse.GetSessionUsageResponseH\x00\x12U\n\x12get_framework_info\x18\x0b \x01(\x0b\x32\x37.livekit.agent.SessionResponse.GetFrameworkInfoResponseH\x00\x12\x44\n\tupdate_io\x18\x0c \x01(\x0b\x32/.livekit.agent.SessionResponse.UpdateIOResponseH\x00\x12X\n\x13\x66inalize_simulation\x18\r \x01(\x0b\x32\x39.livekit.agent.SessionResponse.FinalizeSimulationResponseH\x00\x1a\x06\n\x04Pong\x1aL\n\x16GetChatHistoryResponse\x12\x32\n\x05items\x18\x01 \x03(\x0b\x32#.livekit.agent.ChatContext.ChatItem\x1a\x94\x01\n\x14GetAgentInfoResponse\x12\n\n\x02id\x18\x01 \x01(\t\x12\x19\n\x0cinstructions\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05tools\x18\x03 \x03(\t\x12\x35\n\x08\x63hat_ctx\x18\x04 \x03(\x0b\x32#.livekit.agent.ChatContext.ChatItemB\x0f\n\r_instructions\x1a\x46\n\x10RunInputResponse\x12\x32\n\x05items\x18\x01 \x03(\x0b\x32#.livekit.agent.ChatContext.ChatItem\x1a\xbf\x02\n\x17GetSessionStateResponse\x12.\n\x0b\x61gent_state\x18\x01 \x01(\x0e\x32\x19.livekit.agent.AgentState\x12,\n\nuser_state\x18\x02 \x01(\x0e\x32\x18.livekit.agent.UserState\x12\x10\n\x08\x61gent_id\x18\x03 \x01(\t\x12T\n\x07options\x18\x04 \x03(\x0b\x32\x43.livekit.agent.SessionResponse.GetSessionStateResponse.OptionsEntry\x12.\n\ncreated_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a.\n\x0cOptionsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1az\n\x13GetRTCStatsResponse\x12\x30\n\x0fpublisher_stats\x18\x01 \x03(\x0b\x32\x17.google.protobuf.Struct\x12\x31\n\x10subscriber_stats\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Struct\x1az\n\x17GetSessionUsageResponse\x12/\n\x05usage\x18\x01 \x01(\x0b\x32 .livekit.agent.AgentSessionUsage\x12.\n\ncreated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a<\n\x18GetFrameworkInfoResponse\x12\x0b\n\x03sdk\x18\x01 \x01(\t\x12\x13\n\x0bsdk_version\x18\x02 \x01(\t\x1a\x12\n\x10UpdateIOResponse\x1a\xb5\x01\n\x1a\x46inalizeSimulationResponse\x12\x61\n\x0cuser_verdict\x18\x01 \x01(\x0b\x32K.livekit.agent.SessionResponse.FinalizeSimulationResponse.SimulationVerdict\x1a\x34\n\x11SimulationVerdict\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0e\n\x06reason\x18\x02 \x01(\tB\n\n\x08responseB\x08\n\x06_error\"\xba\x06\n\x13\x41gentSessionMessage\x12N\n\x0b\x61udio_input\x18\x01 \x01(\x0b\x32\x37.livekit.agent.AgentSessionMessage.ConsoleIO.AudioFrameH\x00\x12O\n\x0c\x61udio_output\x18\x02 \x01(\x0b\x32\x37.livekit.agent.AgentSessionMessage.ConsoleIO.AudioFrameH\x00\x12\x31\n\x05\x65vent\x18\x03 \x01(\x0b\x32 .livekit.agent.AgentSessionEventH\x00\x12\x30\n\x07request\x18\x04 \x01(\x0b\x32\x1d.livekit.agent.SessionRequestH\x00\x12\x32\n\x08response\x18\x05 \x01(\x0b\x32\x1e.livekit.agent.SessionResponseH\x00\x12_\n\x14\x61udio_playback_flush\x18\x06 \x01(\x0b\x32?.livekit.agent.AgentSessionMessage.ConsoleIO.AudioPlaybackFlushH\x00\x12_\n\x14\x61udio_playback_clear\x18\x07 \x01(\x0b\x32?.livekit.agent.AgentSessionMessage.ConsoleIO.AudioPlaybackClearH\x00\x12\x65\n\x17\x61udio_playback_finished\x18\x08 \x01(\x0b\x32\x42.livekit.agent.AgentSessionMessage.ConsoleIO.AudioPlaybackFinishedH\x00\x1a\xb4\x01\n\tConsoleIO\x1a\x62\n\nAudioFrame\x12\x0c\n\x04\x64\x61ta\x18\x01 \x01(\x0c\x12\x13\n\x0bsample_rate\x18\x02 \x01(\r\x12\x14\n\x0cnum_channels\x18\x03 \x01(\r\x12\x1b\n\x13samples_per_channel\x18\x04 \x01(\r\x1a\x14\n\x12\x41udioPlaybackFlush\x1a\x14\n\x12\x41udioPlaybackClear\x1a\x17\n\x15\x41udioPlaybackFinishedB\t\n\x07message*>\n\x08\x43hatRole\x12\r\n\tDEVELOPER\x10\x00\x12\n\n\x06SYSTEM\x10\x01\x12\x08\n\x04USER\x10\x02\x12\r\n\tASSISTANT\x10\x03*b\n\nAgentState\x12\x13\n\x0f\x41S_INITIALIZING\x10\x00\x12\x0b\n\x07\x41S_IDLE\x10\x01\x12\x10\n\x0c\x41S_LISTENING\x10\x02\x12\x0f\n\x0b\x41S_THINKING\x10\x03\x12\x0f\n\x0b\x41S_SPEAKING\x10\x04*;\n\tUserState\x12\x0f\n\x0bUS_SPEAKING\x10\x00\x12\x10\n\x0cUS_LISTENING\x10\x01\x12\x0b\n\x07US_AWAY\x10\x02*M\n\x0eToolCallStatus\x12\x0e\n\nTC_RUNNING\x10\x00\x12\x0b\n\x07TC_DONE\x10\x01\x12\x0c\n\x08TC_ERROR\x10\x02\x12\x10\n\x0cTC_CANCELLED\x10\x03*Y\n\x0fToolReplyStatus\x12\x10\n\x0cTR_SCHEDULED\x10\x00\x12\x10\n\x0cTR_COMPLETED\x10\x01\x12\x12\n\x0eTR_INTERRUPTED\x10\x02\x12\x0e\n\nTR_SKIPPED\x10\x03*\x86\x01\n\x0b\x41mdCategory\x12\x0f\n\x0b\x41MD_UNKNOWN\x10\x00\x12\r\n\tAMD_HUMAN\x10\x01\x12\x13\n\x0f\x41MD_MACHINE_IVR\x10\x02\x12\x12\n\x0e\x41MD_MACHINE_VM\x10\x03\x12\x1b\n\x17\x41MD_MACHINE_UNAVAILABLE\x10\x04\x12\x11\n\rAMD_UNCERTAIN\x10\x05\x42+Z)github.com/livekit/protocol/livekit/agentb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent.agent_session', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z)github.com/livekit/protocol/livekit/agent' + _globals['_CHATMESSAGE_EXTRAENTRY']._loaded_options = None + _globals['_CHATMESSAGE_EXTRAENTRY']._serialized_options = b'8\001' + _globals['_FUNCTIONCALL'].fields_by_name['call_id']._loaded_options = None + _globals['_FUNCTIONCALL'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_FUNCTIONCALLOUTPUT'].fields_by_name['call_id']._loaded_options = None + _globals['_FUNCTIONCALLOUTPUT'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_AGENTHANDOFF'].fields_by_name['old_agent_id']._loaded_options = None + _globals['_AGENTHANDOFF'].fields_by_name['old_agent_id']._serialized_options = b'\272P\noldAgentID' + _globals['_AGENTHANDOFF'].fields_by_name['new_agent_id']._loaded_options = None + _globals['_AGENTHANDOFF'].fields_by_name['new_agent_id']._serialized_options = b'\272P\nnewAgentID' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED'].fields_by_name['id']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED'].fields_by_name['id']._serialized_options = b'\272P\002ID' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED'].fields_by_name['call_id']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED'].fields_by_name['id']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED'].fields_by_name['id']._serialized_options = b'\272P\002ID' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED'].fields_by_name['call_id']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED'].fields_by_name['update_ids']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED'].fields_by_name['update_ids']._serialized_options = b'\272P\tupdateIDs' + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED'].fields_by_name['speech_id']._loaded_options = None + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED'].fields_by_name['speech_id']._serialized_options = b'\272P\010speechID' + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE_OPTIONSENTRY']._loaded_options = None + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE_OPTIONSENTRY']._serialized_options = b'8\001' + _globals['_CHATROLE']._serialized_start=11177 + _globals['_CHATROLE']._serialized_end=11239 + _globals['_AGENTSTATE']._serialized_start=11241 + _globals['_AGENTSTATE']._serialized_end=11339 + _globals['_USERSTATE']._serialized_start=11341 + _globals['_USERSTATE']._serialized_end=11400 + _globals['_TOOLCALLSTATUS']._serialized_start=11402 + _globals['_TOOLCALLSTATUS']._serialized_end=11479 + _globals['_TOOLREPLYSTATUS']._serialized_start=11481 + _globals['_TOOLREPLYSTATUS']._serialized_end=11570 + _globals['_AMDCATEGORY']._serialized_start=11573 + _globals['_AMDCATEGORY']._serialized_end=11707 + _globals['_METRICSREPORT']._serialized_start=170 + _globals['_METRICSREPORT']._serialized_end=711 + _globals['_TIMEDSTRING']._serialized_start=714 + _globals['_TIMEDSTRING']._serialized_end=951 + _globals['_CHATMESSAGE']._serialized_start=954 + _globals['_CHATMESSAGE']._serialized_end=1395 + _globals['_CHATMESSAGE_CHATCONTENT']._serialized_start=1283 + _globals['_CHATMESSAGE_CHATCONTENT']._serialized_end=1323 + _globals['_CHATMESSAGE_EXTRAENTRY']._serialized_start=1325 + _globals['_CHATMESSAGE_EXTRAENTRY']._serialized_end=1369 + _globals['_FUNCTIONCALL']._serialized_start=1398 + _globals['_FUNCTIONCALL']._serialized_end=1533 + _globals['_FUNCTIONCALLOUTPUT']._serialized_start=1536 + _globals['_FUNCTIONCALLOUTPUT']._serialized_end=1692 + _globals['_AGENTHANDOFF']._serialized_start=1695 + _globals['_AGENTHANDOFF']._serialized_end=1865 + _globals['_AGENTCONFIGUPDATE']._serialized_start=1868 + _globals['_AGENTCONFIGUPDATE']._serialized_end=2035 + _globals['_CHATCONTEXT']._serialized_start=2038 + _globals['_CHATCONTEXT']._serialized_end=2411 + _globals['_CHATCONTEXT_CHATITEM']._serialized_start=2106 + _globals['_CHATCONTEXT_CHATITEM']._serialized_end=2411 + _globals['_LLMMODELUSAGE']._serialized_start=2414 + _globals['_LLMMODELUSAGE']._serialized_end=2806 + _globals['_TTSMODELUSAGE']._serialized_start=2809 + _globals['_TTSMODELUSAGE']._serialized_end=2952 + _globals['_STTMODELUSAGE']._serialized_start=2954 + _globals['_STTMODELUSAGE']._serialized_end=3071 + _globals['_INTERRUPTIONMODELUSAGE']._serialized_start=3073 + _globals['_INTERRUPTIONMODELUSAGE']._serialized_end=3154 + _globals['_EOTMODELUSAGE']._serialized_start=3156 + _globals['_EOTMODELUSAGE']._serialized_end=3228 + _globals['_MODELUSAGE']._serialized_start=3231 + _globals['_MODELUSAGE']._serialized_end=3495 + _globals['_AGENTSESSIONUSAGE']._serialized_start=3497 + _globals['_AGENTSESSIONUSAGE']._serialized_end=3564 + _globals['_DEBUGMESSAGE']._serialized_start=3566 + _globals['_DEBUGMESSAGE']._serialized_end=3622 + _globals['_AGENTSESSIONEVENT']._serialized_start=3625 + _globals['_AGENTSESSIONEVENT']._serialized_end=6939 + _globals['_AGENTSESSIONEVENT_AGENTSTATECHANGED']._serialized_start=4828 + _globals['_AGENTSESSIONEVENT_AGENTSTATECHANGED']._serialized_end=4939 + _globals['_AGENTSESSIONEVENT_USERSTATECHANGED']._serialized_start=4941 + _globals['_AGENTSESSIONEVENT_USERSTATECHANGED']._serialized_end=5049 + _globals['_AGENTSESSIONEVENT_CONVERSATIONITEMADDED']._serialized_start=5051 + _globals['_AGENTSESSIONEVENT_CONVERSATIONITEMADDED']._serialized_end=5125 + _globals['_AGENTSESSIONEVENT_USERINPUTTRANSCRIBED']._serialized_start=5127 + _globals['_AGENTSESSIONEVENT_USERINPUTTRANSCRIBED']._serialized_end=5223 + _globals['_AGENTSESSIONEVENT_FUNCTIONTOOLSSTARTED']._serialized_start=5225 + _globals['_AGENTSESSIONEVENT_FUNCTIONTOOLSSTARTED']._serialized_end=5300 + _globals['_AGENTSESSIONEVENT_FUNCTIONTOOLSEXECUTED']._serialized_start=5303 + _globals['_AGENTSESSIONEVENT_FUNCTIONTOOLSEXECUTED']._serialized_end=5445 + _globals['_AGENTSESSIONEVENT_ERROR']._serialized_start=5447 + _globals['_AGENTSESSIONEVENT_ERROR']._serialized_end=5471 + _globals['_AGENTSESSIONEVENT_OVERLAPPINGSPEECH']._serialized_start=5474 + _globals['_AGENTSESSIONEVENT_OVERLAPPINGSPEECH']._serialized_end=5676 + _globals['_AGENTSESSIONEVENT_AMDPREDICTION']._serialized_start=5679 + _globals['_AGENTSESSIONEVENT_AMDPREDICTION']._serialized_end=5870 + _globals['_AGENTSESSIONEVENT_EOTPREDICTION']._serialized_start=5873 + _globals['_AGENTSESSIONEVENT_EOTPREDICTION']._serialized_end=6025 + _globals['_AGENTSESSIONEVENT_SESSIONUSAGEUPDATED']._serialized_start=6027 + _globals['_AGENTSESSIONEVENT_SESSIONUSAGEUPDATED']._serialized_end=6097 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED']._serialized_start=6100 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED']._serialized_end=6887 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_STARTED']._serialized_start=6468 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_STARTED']._serialized_end=6529 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED']._serialized_start=6531 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_CALLUPDATED']._serialized_end=6608 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED']._serialized_start=6611 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_ENDED']._serialized_end=6746 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED']._serialized_start=6749 + _globals['_AGENTSESSIONEVENT_TOOLEXECUTIONUPDATED_REPLYUPDATED']._serialized_end=6877 + _globals['_AGENTSESSIONEVENT_AGENTFALSEINTERRUPTION']._serialized_start=6889 + _globals['_AGENTSESSIONEVENT_AGENTFALSEINTERRUPTION']._serialized_end=6930 + _globals['_SESSIONREQUEST']._serialized_start=6942 + _globals['_SESSIONREQUEST']._serialized_end=8340 + _globals['_SESSIONREQUEST_PING']._serialized_start=7677 + _globals['_SESSIONREQUEST_PING']._serialized_end=7683 + _globals['_SESSIONREQUEST_GETCHATHISTORY']._serialized_start=7685 + _globals['_SESSIONREQUEST_GETCHATHISTORY']._serialized_end=7701 + _globals['_SESSIONREQUEST_RUNINPUT']._serialized_start=7703 + _globals['_SESSIONREQUEST_RUNINPUT']._serialized_end=7727 + _globals['_SESSIONREQUEST_GETAGENTINFO']._serialized_start=7729 + _globals['_SESSIONREQUEST_GETAGENTINFO']._serialized_end=7743 + _globals['_SESSIONREQUEST_GETSESSIONSTATE']._serialized_start=7745 + _globals['_SESSIONREQUEST_GETSESSIONSTATE']._serialized_end=7762 + _globals['_SESSIONREQUEST_GETRTCSTATS']._serialized_start=7764 + _globals['_SESSIONREQUEST_GETRTCSTATS']._serialized_end=7777 + _globals['_SESSIONREQUEST_GETSESSIONUSAGE']._serialized_start=7779 + _globals['_SESSIONREQUEST_GETSESSIONUSAGE']._serialized_end=7796 + _globals['_SESSIONREQUEST_GETFRAMEWORKINFO']._serialized_start=7798 + _globals['_SESSIONREQUEST_GETFRAMEWORKINFO']._serialized_end=7816 + _globals['_SESSIONREQUEST_UPDATEIO']._serialized_start=7819 + _globals['_SESSIONREQUEST_UPDATEIO']._serialized_end=8250 + _globals['_SESSIONREQUEST_UPDATEIO_INPUT']._serialized_start=7965 + _globals['_SESSIONREQUEST_UPDATEIO_INPUT']._serialized_end=8064 + _globals['_SESSIONREQUEST_UPDATEIO_OUTPUT']._serialized_start=8067 + _globals['_SESSIONREQUEST_UPDATEIO_OUTPUT']._serialized_end=8229 + _globals['_SESSIONREQUEST_FINALIZESIMULATION']._serialized_start=8252 + _globals['_SESSIONREQUEST_FINALIZESIMULATION']._serialized_end=8329 + _globals['_SESSIONRESPONSE']._serialized_start=8343 + _globals['_SESSIONRESPONSE']._serialized_end=10346 + _globals['_SESSIONRESPONSE_PONG']._serialized_start=9181 + _globals['_SESSIONRESPONSE_PONG']._serialized_end=9187 + _globals['_SESSIONRESPONSE_GETCHATHISTORYRESPONSE']._serialized_start=9189 + _globals['_SESSIONRESPONSE_GETCHATHISTORYRESPONSE']._serialized_end=9265 + _globals['_SESSIONRESPONSE_GETAGENTINFORESPONSE']._serialized_start=9268 + _globals['_SESSIONRESPONSE_GETAGENTINFORESPONSE']._serialized_end=9416 + _globals['_SESSIONRESPONSE_RUNINPUTRESPONSE']._serialized_start=9418 + _globals['_SESSIONRESPONSE_RUNINPUTRESPONSE']._serialized_end=9488 + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE']._serialized_start=9491 + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE']._serialized_end=9810 + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE_OPTIONSENTRY']._serialized_start=9764 + _globals['_SESSIONRESPONSE_GETSESSIONSTATERESPONSE_OPTIONSENTRY']._serialized_end=9810 + _globals['_SESSIONRESPONSE_GETRTCSTATSRESPONSE']._serialized_start=9812 + _globals['_SESSIONRESPONSE_GETRTCSTATSRESPONSE']._serialized_end=9934 + _globals['_SESSIONRESPONSE_GETSESSIONUSAGERESPONSE']._serialized_start=9936 + _globals['_SESSIONRESPONSE_GETSESSIONUSAGERESPONSE']._serialized_end=10058 + _globals['_SESSIONRESPONSE_GETFRAMEWORKINFORESPONSE']._serialized_start=10060 + _globals['_SESSIONRESPONSE_GETFRAMEWORKINFORESPONSE']._serialized_end=10120 + _globals['_SESSIONRESPONSE_UPDATEIORESPONSE']._serialized_start=10122 + _globals['_SESSIONRESPONSE_UPDATEIORESPONSE']._serialized_end=10140 + _globals['_SESSIONRESPONSE_FINALIZESIMULATIONRESPONSE']._serialized_start=10143 + _globals['_SESSIONRESPONSE_FINALIZESIMULATIONRESPONSE']._serialized_end=10324 + _globals['_SESSIONRESPONSE_FINALIZESIMULATIONRESPONSE_SIMULATIONVERDICT']._serialized_start=10272 + _globals['_SESSIONRESPONSE_FINALIZESIMULATIONRESPONSE_SIMULATIONVERDICT']._serialized_end=10324 + _globals['_AGENTSESSIONMESSAGE']._serialized_start=10349 + _globals['_AGENTSESSIONMESSAGE']._serialized_end=11175 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO']._serialized_start=10984 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO']._serialized_end=11164 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOFRAME']._serialized_start=10997 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOFRAME']._serialized_end=11095 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKFLUSH']._serialized_start=11097 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKFLUSH']._serialized_end=11117 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKCLEAR']._serialized_start=11119 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKCLEAR']._serialized_end=11139 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKFINISHED']._serialized_start=11141 + _globals['_AGENTSESSIONMESSAGE_CONSOLEIO_AUDIOPLAYBACKFINISHED']._serialized_end=11164 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_session.pyi b/livekit-protocol/livekit/protocol/agent_pb/agent_session.pyi new file mode 100644 index 00000000..aa78fd86 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_session.pyi @@ -0,0 +1,738 @@ +from google.protobuf import struct_pb2 as _struct_pb2 +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from google.protobuf import duration_pb2 as _duration_pb2 +from ..logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ChatRole(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DEVELOPER: _ClassVar[ChatRole] + SYSTEM: _ClassVar[ChatRole] + USER: _ClassVar[ChatRole] + ASSISTANT: _ClassVar[ChatRole] + +class AgentState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AS_INITIALIZING: _ClassVar[AgentState] + AS_IDLE: _ClassVar[AgentState] + AS_LISTENING: _ClassVar[AgentState] + AS_THINKING: _ClassVar[AgentState] + AS_SPEAKING: _ClassVar[AgentState] + +class UserState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + US_SPEAKING: _ClassVar[UserState] + US_LISTENING: _ClassVar[UserState] + US_AWAY: _ClassVar[UserState] + +class ToolCallStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + TC_RUNNING: _ClassVar[ToolCallStatus] + TC_DONE: _ClassVar[ToolCallStatus] + TC_ERROR: _ClassVar[ToolCallStatus] + TC_CANCELLED: _ClassVar[ToolCallStatus] + +class ToolReplyStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + TR_SCHEDULED: _ClassVar[ToolReplyStatus] + TR_COMPLETED: _ClassVar[ToolReplyStatus] + TR_INTERRUPTED: _ClassVar[ToolReplyStatus] + TR_SKIPPED: _ClassVar[ToolReplyStatus] + +class AmdCategory(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AMD_UNKNOWN: _ClassVar[AmdCategory] + AMD_HUMAN: _ClassVar[AmdCategory] + AMD_MACHINE_IVR: _ClassVar[AmdCategory] + AMD_MACHINE_VM: _ClassVar[AmdCategory] + AMD_MACHINE_UNAVAILABLE: _ClassVar[AmdCategory] + AMD_UNCERTAIN: _ClassVar[AmdCategory] +DEVELOPER: ChatRole +SYSTEM: ChatRole +USER: ChatRole +ASSISTANT: ChatRole +AS_INITIALIZING: AgentState +AS_IDLE: AgentState +AS_LISTENING: AgentState +AS_THINKING: AgentState +AS_SPEAKING: AgentState +US_SPEAKING: UserState +US_LISTENING: UserState +US_AWAY: UserState +TC_RUNNING: ToolCallStatus +TC_DONE: ToolCallStatus +TC_ERROR: ToolCallStatus +TC_CANCELLED: ToolCallStatus +TR_SCHEDULED: ToolReplyStatus +TR_COMPLETED: ToolReplyStatus +TR_INTERRUPTED: ToolReplyStatus +TR_SKIPPED: ToolReplyStatus +AMD_UNKNOWN: AmdCategory +AMD_HUMAN: AmdCategory +AMD_MACHINE_IVR: AmdCategory +AMD_MACHINE_VM: AmdCategory +AMD_MACHINE_UNAVAILABLE: AmdCategory +AMD_UNCERTAIN: AmdCategory + +class MetricsReport(_message.Message): + __slots__ = ("started_speaking_at", "stopped_speaking_at", "transcription_delay", "end_of_turn_delay", "on_user_turn_completed_delay", "llm_node_ttft", "tts_node_ttfb", "e2e_latency", "llm_node_tps", "llm_node_ttfs") + STARTED_SPEAKING_AT_FIELD_NUMBER: _ClassVar[int] + STOPPED_SPEAKING_AT_FIELD_NUMBER: _ClassVar[int] + TRANSCRIPTION_DELAY_FIELD_NUMBER: _ClassVar[int] + END_OF_TURN_DELAY_FIELD_NUMBER: _ClassVar[int] + ON_USER_TURN_COMPLETED_DELAY_FIELD_NUMBER: _ClassVar[int] + LLM_NODE_TTFT_FIELD_NUMBER: _ClassVar[int] + TTS_NODE_TTFB_FIELD_NUMBER: _ClassVar[int] + E2E_LATENCY_FIELD_NUMBER: _ClassVar[int] + LLM_NODE_TPS_FIELD_NUMBER: _ClassVar[int] + LLM_NODE_TTFS_FIELD_NUMBER: _ClassVar[int] + started_speaking_at: _timestamp_pb2.Timestamp + stopped_speaking_at: _timestamp_pb2.Timestamp + transcription_delay: float + end_of_turn_delay: float + on_user_turn_completed_delay: float + llm_node_ttft: float + tts_node_ttfb: float + e2e_latency: float + llm_node_tps: float + llm_node_ttfs: float + def __init__(self, started_speaking_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., stopped_speaking_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., transcription_delay: _Optional[float] = ..., end_of_turn_delay: _Optional[float] = ..., on_user_turn_completed_delay: _Optional[float] = ..., llm_node_ttft: _Optional[float] = ..., tts_node_ttfb: _Optional[float] = ..., e2e_latency: _Optional[float] = ..., llm_node_tps: _Optional[float] = ..., llm_node_ttfs: _Optional[float] = ...) -> None: ... + +class TimedString(_message.Message): + __slots__ = ("text", "start_time", "end_time", "confidence", "start_time_offset", "speaker_id") + TEXT_FIELD_NUMBER: _ClassVar[int] + START_TIME_FIELD_NUMBER: _ClassVar[int] + END_TIME_FIELD_NUMBER: _ClassVar[int] + CONFIDENCE_FIELD_NUMBER: _ClassVar[int] + START_TIME_OFFSET_FIELD_NUMBER: _ClassVar[int] + SPEAKER_ID_FIELD_NUMBER: _ClassVar[int] + text: str + start_time: float + end_time: float + confidence: float + start_time_offset: float + speaker_id: str + def __init__(self, text: _Optional[str] = ..., start_time: _Optional[float] = ..., end_time: _Optional[float] = ..., confidence: _Optional[float] = ..., start_time_offset: _Optional[float] = ..., speaker_id: _Optional[str] = ...) -> None: ... + +class ChatMessage(_message.Message): + __slots__ = ("id", "role", "content", "interrupted", "transcript_confidence", "extra", "metrics", "created_at") + class ChatContent(_message.Message): + __slots__ = ("text",) + TEXT_FIELD_NUMBER: _ClassVar[int] + text: str + def __init__(self, text: _Optional[str] = ...) -> None: ... + class ExtraEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + ID_FIELD_NUMBER: _ClassVar[int] + ROLE_FIELD_NUMBER: _ClassVar[int] + CONTENT_FIELD_NUMBER: _ClassVar[int] + INTERRUPTED_FIELD_NUMBER: _ClassVar[int] + TRANSCRIPT_CONFIDENCE_FIELD_NUMBER: _ClassVar[int] + EXTRA_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: str + role: ChatRole + content: _containers.RepeatedCompositeFieldContainer[ChatMessage.ChatContent] + interrupted: bool + transcript_confidence: float + extra: _containers.ScalarMap[str, str] + metrics: MetricsReport + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[str] = ..., role: _Optional[_Union[ChatRole, str]] = ..., content: _Optional[_Iterable[_Union[ChatMessage.ChatContent, _Mapping]]] = ..., interrupted: bool = ..., transcript_confidence: _Optional[float] = ..., extra: _Optional[_Mapping[str, str]] = ..., metrics: _Optional[_Union[MetricsReport, _Mapping]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class FunctionCall(_message.Message): + __slots__ = ("id", "call_id", "arguments", "name", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + CALL_ID_FIELD_NUMBER: _ClassVar[int] + ARGUMENTS_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: str + call_id: str + arguments: str + name: str + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[str] = ..., call_id: _Optional[str] = ..., arguments: _Optional[str] = ..., name: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class FunctionCallOutput(_message.Message): + __slots__ = ("id", "name", "call_id", "output", "is_error", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + CALL_ID_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] + IS_ERROR_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: str + name: str + call_id: str + output: str + is_error: bool + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., call_id: _Optional[str] = ..., output: _Optional[str] = ..., is_error: bool = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class AgentHandoff(_message.Message): + __slots__ = ("id", "old_agent_id", "new_agent_id", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + OLD_AGENT_ID_FIELD_NUMBER: _ClassVar[int] + NEW_AGENT_ID_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: str + old_agent_id: str + new_agent_id: str + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[str] = ..., old_agent_id: _Optional[str] = ..., new_agent_id: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class AgentConfigUpdate(_message.Message): + __slots__ = ("id", "instructions", "tools_added", "tools_removed", "created_at") + ID_FIELD_NUMBER: _ClassVar[int] + INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int] + TOOLS_ADDED_FIELD_NUMBER: _ClassVar[int] + TOOLS_REMOVED_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + id: str + instructions: str + tools_added: _containers.RepeatedScalarFieldContainer[str] + tools_removed: _containers.RepeatedScalarFieldContainer[str] + created_at: _timestamp_pb2.Timestamp + def __init__(self, id: _Optional[str] = ..., instructions: _Optional[str] = ..., tools_added: _Optional[_Iterable[str]] = ..., tools_removed: _Optional[_Iterable[str]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class ChatContext(_message.Message): + __slots__ = ("items",) + class ChatItem(_message.Message): + __slots__ = ("message", "function_call", "function_call_output", "agent_handoff", "agent_config_update") + MESSAGE_FIELD_NUMBER: _ClassVar[int] + FUNCTION_CALL_FIELD_NUMBER: _ClassVar[int] + FUNCTION_CALL_OUTPUT_FIELD_NUMBER: _ClassVar[int] + AGENT_HANDOFF_FIELD_NUMBER: _ClassVar[int] + AGENT_CONFIG_UPDATE_FIELD_NUMBER: _ClassVar[int] + message: ChatMessage + function_call: FunctionCall + function_call_output: FunctionCallOutput + agent_handoff: AgentHandoff + agent_config_update: AgentConfigUpdate + def __init__(self, message: _Optional[_Union[ChatMessage, _Mapping]] = ..., function_call: _Optional[_Union[FunctionCall, _Mapping]] = ..., function_call_output: _Optional[_Union[FunctionCallOutput, _Mapping]] = ..., agent_handoff: _Optional[_Union[AgentHandoff, _Mapping]] = ..., agent_config_update: _Optional[_Union[AgentConfigUpdate, _Mapping]] = ...) -> None: ... + ITEMS_FIELD_NUMBER: _ClassVar[int] + items: _containers.RepeatedCompositeFieldContainer[ChatContext.ChatItem] + def __init__(self, items: _Optional[_Iterable[_Union[ChatContext.ChatItem, _Mapping]]] = ...) -> None: ... + +class LLMModelUsage(_message.Message): + __slots__ = ("provider", "model", "input_tokens", "input_cached_tokens", "input_audio_tokens", "input_cached_audio_tokens", "input_text_tokens", "input_cached_text_tokens", "input_image_tokens", "input_cached_image_tokens", "output_tokens", "output_audio_tokens", "output_text_tokens", "session_duration") + PROVIDER_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + INPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_CACHED_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_AUDIO_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_CACHED_AUDIO_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_TEXT_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_CACHED_TEXT_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_IMAGE_TOKENS_FIELD_NUMBER: _ClassVar[int] + INPUT_CACHED_IMAGE_TOKENS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_AUDIO_TOKENS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_TEXT_TOKENS_FIELD_NUMBER: _ClassVar[int] + SESSION_DURATION_FIELD_NUMBER: _ClassVar[int] + provider: str + model: str + input_tokens: int + input_cached_tokens: int + input_audio_tokens: int + input_cached_audio_tokens: int + input_text_tokens: int + input_cached_text_tokens: int + input_image_tokens: int + input_cached_image_tokens: int + output_tokens: int + output_audio_tokens: int + output_text_tokens: int + session_duration: float + def __init__(self, provider: _Optional[str] = ..., model: _Optional[str] = ..., input_tokens: _Optional[int] = ..., input_cached_tokens: _Optional[int] = ..., input_audio_tokens: _Optional[int] = ..., input_cached_audio_tokens: _Optional[int] = ..., input_text_tokens: _Optional[int] = ..., input_cached_text_tokens: _Optional[int] = ..., input_image_tokens: _Optional[int] = ..., input_cached_image_tokens: _Optional[int] = ..., output_tokens: _Optional[int] = ..., output_audio_tokens: _Optional[int] = ..., output_text_tokens: _Optional[int] = ..., session_duration: _Optional[float] = ...) -> None: ... + +class TTSModelUsage(_message.Message): + __slots__ = ("provider", "model", "input_tokens", "output_tokens", "characters_count", "audio_duration") + PROVIDER_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + INPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + CHARACTERS_COUNT_FIELD_NUMBER: _ClassVar[int] + AUDIO_DURATION_FIELD_NUMBER: _ClassVar[int] + provider: str + model: str + input_tokens: int + output_tokens: int + characters_count: int + audio_duration: float + def __init__(self, provider: _Optional[str] = ..., model: _Optional[str] = ..., input_tokens: _Optional[int] = ..., output_tokens: _Optional[int] = ..., characters_count: _Optional[int] = ..., audio_duration: _Optional[float] = ...) -> None: ... + +class STTModelUsage(_message.Message): + __slots__ = ("provider", "model", "input_tokens", "output_tokens", "audio_duration") + PROVIDER_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + INPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + OUTPUT_TOKENS_FIELD_NUMBER: _ClassVar[int] + AUDIO_DURATION_FIELD_NUMBER: _ClassVar[int] + provider: str + model: str + input_tokens: int + output_tokens: int + audio_duration: float + def __init__(self, provider: _Optional[str] = ..., model: _Optional[str] = ..., input_tokens: _Optional[int] = ..., output_tokens: _Optional[int] = ..., audio_duration: _Optional[float] = ...) -> None: ... + +class InterruptionModelUsage(_message.Message): + __slots__ = ("provider", "model", "total_requests") + PROVIDER_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + TOTAL_REQUESTS_FIELD_NUMBER: _ClassVar[int] + provider: str + model: str + total_requests: int + def __init__(self, provider: _Optional[str] = ..., model: _Optional[str] = ..., total_requests: _Optional[int] = ...) -> None: ... + +class EotModelUsage(_message.Message): + __slots__ = ("provider", "model", "total_requests") + PROVIDER_FIELD_NUMBER: _ClassVar[int] + MODEL_FIELD_NUMBER: _ClassVar[int] + TOTAL_REQUESTS_FIELD_NUMBER: _ClassVar[int] + provider: str + model: str + total_requests: int + def __init__(self, provider: _Optional[str] = ..., model: _Optional[str] = ..., total_requests: _Optional[int] = ...) -> None: ... + +class ModelUsage(_message.Message): + __slots__ = ("llm", "tts", "stt", "interruption", "eot") + LLM_FIELD_NUMBER: _ClassVar[int] + TTS_FIELD_NUMBER: _ClassVar[int] + STT_FIELD_NUMBER: _ClassVar[int] + INTERRUPTION_FIELD_NUMBER: _ClassVar[int] + EOT_FIELD_NUMBER: _ClassVar[int] + llm: LLMModelUsage + tts: TTSModelUsage + stt: STTModelUsage + interruption: InterruptionModelUsage + eot: EotModelUsage + def __init__(self, llm: _Optional[_Union[LLMModelUsage, _Mapping]] = ..., tts: _Optional[_Union[TTSModelUsage, _Mapping]] = ..., stt: _Optional[_Union[STTModelUsage, _Mapping]] = ..., interruption: _Optional[_Union[InterruptionModelUsage, _Mapping]] = ..., eot: _Optional[_Union[EotModelUsage, _Mapping]] = ...) -> None: ... + +class AgentSessionUsage(_message.Message): + __slots__ = ("model_usage",) + MODEL_USAGE_FIELD_NUMBER: _ClassVar[int] + model_usage: _containers.RepeatedCompositeFieldContainer[ModelUsage] + def __init__(self, model_usage: _Optional[_Iterable[_Union[ModelUsage, _Mapping]]] = ...) -> None: ... + +class DebugMessage(_message.Message): + __slots__ = ("payload",) + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + payload: _struct_pb2.Struct + def __init__(self, payload: _Optional[_Union[_struct_pb2.Struct, _Mapping]] = ...) -> None: ... + +class AgentSessionEvent(_message.Message): + __slots__ = ("created_at", "agent_state_changed", "user_state_changed", "conversation_item_added", "user_input_transcribed", "function_tools_executed", "error", "overlapping_speech", "session_usage_updated", "amd_prediction", "eot_prediction", "function_tools_started", "debug_message", "tool_execution_updated", "agent_false_interruption") + class AgentStateChanged(_message.Message): + __slots__ = ("old_state", "new_state") + OLD_STATE_FIELD_NUMBER: _ClassVar[int] + NEW_STATE_FIELD_NUMBER: _ClassVar[int] + old_state: AgentState + new_state: AgentState + def __init__(self, old_state: _Optional[_Union[AgentState, str]] = ..., new_state: _Optional[_Union[AgentState, str]] = ...) -> None: ... + class UserStateChanged(_message.Message): + __slots__ = ("old_state", "new_state") + OLD_STATE_FIELD_NUMBER: _ClassVar[int] + NEW_STATE_FIELD_NUMBER: _ClassVar[int] + old_state: UserState + new_state: UserState + def __init__(self, old_state: _Optional[_Union[UserState, str]] = ..., new_state: _Optional[_Union[UserState, str]] = ...) -> None: ... + class ConversationItemAdded(_message.Message): + __slots__ = ("item",) + ITEM_FIELD_NUMBER: _ClassVar[int] + item: ChatContext.ChatItem + def __init__(self, item: _Optional[_Union[ChatContext.ChatItem, _Mapping]] = ...) -> None: ... + class UserInputTranscribed(_message.Message): + __slots__ = ("transcript", "is_final", "language") + TRANSCRIPT_FIELD_NUMBER: _ClassVar[int] + IS_FINAL_FIELD_NUMBER: _ClassVar[int] + LANGUAGE_FIELD_NUMBER: _ClassVar[int] + transcript: str + is_final: bool + language: str + def __init__(self, transcript: _Optional[str] = ..., is_final: bool = ..., language: _Optional[str] = ...) -> None: ... + class FunctionToolsStarted(_message.Message): + __slots__ = ("function_calls",) + FUNCTION_CALLS_FIELD_NUMBER: _ClassVar[int] + function_calls: _containers.RepeatedCompositeFieldContainer[FunctionCall] + def __init__(self, function_calls: _Optional[_Iterable[_Union[FunctionCall, _Mapping]]] = ...) -> None: ... + class FunctionToolsExecuted(_message.Message): + __slots__ = ("function_calls", "function_call_outputs") + FUNCTION_CALLS_FIELD_NUMBER: _ClassVar[int] + FUNCTION_CALL_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + function_calls: _containers.RepeatedCompositeFieldContainer[FunctionCall] + function_call_outputs: _containers.RepeatedCompositeFieldContainer[FunctionCallOutput] + def __init__(self, function_calls: _Optional[_Iterable[_Union[FunctionCall, _Mapping]]] = ..., function_call_outputs: _Optional[_Iterable[_Union[FunctionCallOutput, _Mapping]]] = ...) -> None: ... + class Error(_message.Message): + __slots__ = ("message",) + MESSAGE_FIELD_NUMBER: _ClassVar[int] + message: str + def __init__(self, message: _Optional[str] = ...) -> None: ... + class OverlappingSpeech(_message.Message): + __slots__ = ("is_interruption", "overlap_started_at", "detection_delay", "detected_at") + IS_INTERRUPTION_FIELD_NUMBER: _ClassVar[int] + OVERLAP_STARTED_AT_FIELD_NUMBER: _ClassVar[int] + DETECTION_DELAY_FIELD_NUMBER: _ClassVar[int] + DETECTED_AT_FIELD_NUMBER: _ClassVar[int] + is_interruption: bool + overlap_started_at: _timestamp_pb2.Timestamp + detection_delay: float + detected_at: _timestamp_pb2.Timestamp + def __init__(self, is_interruption: bool = ..., overlap_started_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., detection_delay: _Optional[float] = ..., detected_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + class AmdPrediction(_message.Message): + __slots__ = ("speech_duration", "category", "reason", "transcript", "delay") + SPEECH_DURATION_FIELD_NUMBER: _ClassVar[int] + CATEGORY_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + TRANSCRIPT_FIELD_NUMBER: _ClassVar[int] + DELAY_FIELD_NUMBER: _ClassVar[int] + speech_duration: _duration_pb2.Duration + category: AmdCategory + reason: str + transcript: str + delay: _duration_pb2.Duration + def __init__(self, speech_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., category: _Optional[_Union[AmdCategory, str]] = ..., reason: _Optional[str] = ..., transcript: _Optional[str] = ..., delay: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + class EotPrediction(_message.Message): + __slots__ = ("probability", "threshold", "inference_duration", "delay") + PROBABILITY_FIELD_NUMBER: _ClassVar[int] + THRESHOLD_FIELD_NUMBER: _ClassVar[int] + INFERENCE_DURATION_FIELD_NUMBER: _ClassVar[int] + DELAY_FIELD_NUMBER: _ClassVar[int] + probability: float + threshold: float + inference_duration: _duration_pb2.Duration + delay: _duration_pb2.Duration + def __init__(self, probability: _Optional[float] = ..., threshold: _Optional[float] = ..., inference_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., delay: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + class SessionUsageUpdated(_message.Message): + __slots__ = ("usage",) + USAGE_FIELD_NUMBER: _ClassVar[int] + usage: AgentSessionUsage + def __init__(self, usage: _Optional[_Union[AgentSessionUsage, _Mapping]] = ...) -> None: ... + class ToolExecutionUpdated(_message.Message): + __slots__ = ("started", "call_updated", "reply_updated", "ended") + class Started(_message.Message): + __slots__ = ("function_call",) + FUNCTION_CALL_FIELD_NUMBER: _ClassVar[int] + function_call: FunctionCall + def __init__(self, function_call: _Optional[_Union[FunctionCall, _Mapping]] = ...) -> None: ... + class CallUpdated(_message.Message): + __slots__ = ("id", "call_id", "message") + ID_FIELD_NUMBER: _ClassVar[int] + CALL_ID_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + id: str + call_id: str + message: str + def __init__(self, id: _Optional[str] = ..., call_id: _Optional[str] = ..., message: _Optional[str] = ...) -> None: ... + class Ended(_message.Message): + __slots__ = ("id", "call_id", "message", "status") + ID_FIELD_NUMBER: _ClassVar[int] + CALL_ID_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + id: str + call_id: str + message: str + status: ToolCallStatus + def __init__(self, id: _Optional[str] = ..., call_id: _Optional[str] = ..., message: _Optional[str] = ..., status: _Optional[_Union[ToolCallStatus, str]] = ...) -> None: ... + class ReplyUpdated(_message.Message): + __slots__ = ("update_ids", "status", "speech_id") + UPDATE_IDS_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + SPEECH_ID_FIELD_NUMBER: _ClassVar[int] + update_ids: _containers.RepeatedScalarFieldContainer[str] + status: ToolReplyStatus + speech_id: str + def __init__(self, update_ids: _Optional[_Iterable[str]] = ..., status: _Optional[_Union[ToolReplyStatus, str]] = ..., speech_id: _Optional[str] = ...) -> None: ... + STARTED_FIELD_NUMBER: _ClassVar[int] + CALL_UPDATED_FIELD_NUMBER: _ClassVar[int] + REPLY_UPDATED_FIELD_NUMBER: _ClassVar[int] + ENDED_FIELD_NUMBER: _ClassVar[int] + started: AgentSessionEvent.ToolExecutionUpdated.Started + call_updated: AgentSessionEvent.ToolExecutionUpdated.CallUpdated + reply_updated: AgentSessionEvent.ToolExecutionUpdated.ReplyUpdated + ended: AgentSessionEvent.ToolExecutionUpdated.Ended + def __init__(self, started: _Optional[_Union[AgentSessionEvent.ToolExecutionUpdated.Started, _Mapping]] = ..., call_updated: _Optional[_Union[AgentSessionEvent.ToolExecutionUpdated.CallUpdated, _Mapping]] = ..., reply_updated: _Optional[_Union[AgentSessionEvent.ToolExecutionUpdated.ReplyUpdated, _Mapping]] = ..., ended: _Optional[_Union[AgentSessionEvent.ToolExecutionUpdated.Ended, _Mapping]] = ...) -> None: ... + class AgentFalseInterruption(_message.Message): + __slots__ = ("resumed",) + RESUMED_FIELD_NUMBER: _ClassVar[int] + resumed: bool + def __init__(self, resumed: bool = ...) -> None: ... + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + AGENT_STATE_CHANGED_FIELD_NUMBER: _ClassVar[int] + USER_STATE_CHANGED_FIELD_NUMBER: _ClassVar[int] + CONVERSATION_ITEM_ADDED_FIELD_NUMBER: _ClassVar[int] + USER_INPUT_TRANSCRIBED_FIELD_NUMBER: _ClassVar[int] + FUNCTION_TOOLS_EXECUTED_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + OVERLAPPING_SPEECH_FIELD_NUMBER: _ClassVar[int] + SESSION_USAGE_UPDATED_FIELD_NUMBER: _ClassVar[int] + AMD_PREDICTION_FIELD_NUMBER: _ClassVar[int] + EOT_PREDICTION_FIELD_NUMBER: _ClassVar[int] + FUNCTION_TOOLS_STARTED_FIELD_NUMBER: _ClassVar[int] + DEBUG_MESSAGE_FIELD_NUMBER: _ClassVar[int] + TOOL_EXECUTION_UPDATED_FIELD_NUMBER: _ClassVar[int] + AGENT_FALSE_INTERRUPTION_FIELD_NUMBER: _ClassVar[int] + created_at: _timestamp_pb2.Timestamp + agent_state_changed: AgentSessionEvent.AgentStateChanged + user_state_changed: AgentSessionEvent.UserStateChanged + conversation_item_added: AgentSessionEvent.ConversationItemAdded + user_input_transcribed: AgentSessionEvent.UserInputTranscribed + function_tools_executed: AgentSessionEvent.FunctionToolsExecuted + error: AgentSessionEvent.Error + overlapping_speech: AgentSessionEvent.OverlappingSpeech + session_usage_updated: AgentSessionEvent.SessionUsageUpdated + amd_prediction: AgentSessionEvent.AmdPrediction + eot_prediction: AgentSessionEvent.EotPrediction + function_tools_started: AgentSessionEvent.FunctionToolsStarted + debug_message: DebugMessage + tool_execution_updated: AgentSessionEvent.ToolExecutionUpdated + agent_false_interruption: AgentSessionEvent.AgentFalseInterruption + def __init__(self, created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., agent_state_changed: _Optional[_Union[AgentSessionEvent.AgentStateChanged, _Mapping]] = ..., user_state_changed: _Optional[_Union[AgentSessionEvent.UserStateChanged, _Mapping]] = ..., conversation_item_added: _Optional[_Union[AgentSessionEvent.ConversationItemAdded, _Mapping]] = ..., user_input_transcribed: _Optional[_Union[AgentSessionEvent.UserInputTranscribed, _Mapping]] = ..., function_tools_executed: _Optional[_Union[AgentSessionEvent.FunctionToolsExecuted, _Mapping]] = ..., error: _Optional[_Union[AgentSessionEvent.Error, _Mapping]] = ..., overlapping_speech: _Optional[_Union[AgentSessionEvent.OverlappingSpeech, _Mapping]] = ..., session_usage_updated: _Optional[_Union[AgentSessionEvent.SessionUsageUpdated, _Mapping]] = ..., amd_prediction: _Optional[_Union[AgentSessionEvent.AmdPrediction, _Mapping]] = ..., eot_prediction: _Optional[_Union[AgentSessionEvent.EotPrediction, _Mapping]] = ..., function_tools_started: _Optional[_Union[AgentSessionEvent.FunctionToolsStarted, _Mapping]] = ..., debug_message: _Optional[_Union[DebugMessage, _Mapping]] = ..., tool_execution_updated: _Optional[_Union[AgentSessionEvent.ToolExecutionUpdated, _Mapping]] = ..., agent_false_interruption: _Optional[_Union[AgentSessionEvent.AgentFalseInterruption, _Mapping]] = ...) -> None: ... + +class SessionRequest(_message.Message): + __slots__ = ("request_id", "ping", "get_chat_history", "run_input", "get_agent_info", "get_session_state", "get_rtc_stats", "get_session_usage", "get_framework_info", "update_io", "finalize_simulation") + class Ping(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetChatHistory(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class RunInput(_message.Message): + __slots__ = ("text",) + TEXT_FIELD_NUMBER: _ClassVar[int] + text: str + def __init__(self, text: _Optional[str] = ...) -> None: ... + class GetAgentInfo(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetSessionState(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetRTCStats(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetSessionUsage(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetFrameworkInfo(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class UpdateIO(_message.Message): + __slots__ = ("input", "output") + class Input(_message.Message): + __slots__ = ("audio_enabled", "video_enabled") + AUDIO_ENABLED_FIELD_NUMBER: _ClassVar[int] + VIDEO_ENABLED_FIELD_NUMBER: _ClassVar[int] + audio_enabled: bool + video_enabled: bool + def __init__(self, audio_enabled: bool = ..., video_enabled: bool = ...) -> None: ... + class Output(_message.Message): + __slots__ = ("audio_enabled", "video_enabled", "transcription_enabled") + AUDIO_ENABLED_FIELD_NUMBER: _ClassVar[int] + VIDEO_ENABLED_FIELD_NUMBER: _ClassVar[int] + TRANSCRIPTION_ENABLED_FIELD_NUMBER: _ClassVar[int] + audio_enabled: bool + video_enabled: bool + transcription_enabled: bool + def __init__(self, audio_enabled: bool = ..., video_enabled: bool = ..., transcription_enabled: bool = ...) -> None: ... + INPUT_FIELD_NUMBER: _ClassVar[int] + OUTPUT_FIELD_NUMBER: _ClassVar[int] + input: SessionRequest.UpdateIO.Input + output: SessionRequest.UpdateIO.Output + def __init__(self, input: _Optional[_Union[SessionRequest.UpdateIO.Input, _Mapping]] = ..., output: _Optional[_Union[SessionRequest.UpdateIO.Output, _Mapping]] = ...) -> None: ... + class FinalizeSimulation(_message.Message): + __slots__ = ("provisional_success", "provisional_reason") + PROVISIONAL_SUCCESS_FIELD_NUMBER: _ClassVar[int] + PROVISIONAL_REASON_FIELD_NUMBER: _ClassVar[int] + provisional_success: bool + provisional_reason: str + def __init__(self, provisional_success: bool = ..., provisional_reason: _Optional[str] = ...) -> None: ... + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + PING_FIELD_NUMBER: _ClassVar[int] + GET_CHAT_HISTORY_FIELD_NUMBER: _ClassVar[int] + RUN_INPUT_FIELD_NUMBER: _ClassVar[int] + GET_AGENT_INFO_FIELD_NUMBER: _ClassVar[int] + GET_SESSION_STATE_FIELD_NUMBER: _ClassVar[int] + GET_RTC_STATS_FIELD_NUMBER: _ClassVar[int] + GET_SESSION_USAGE_FIELD_NUMBER: _ClassVar[int] + GET_FRAMEWORK_INFO_FIELD_NUMBER: _ClassVar[int] + UPDATE_IO_FIELD_NUMBER: _ClassVar[int] + FINALIZE_SIMULATION_FIELD_NUMBER: _ClassVar[int] + request_id: str + ping: SessionRequest.Ping + get_chat_history: SessionRequest.GetChatHistory + run_input: SessionRequest.RunInput + get_agent_info: SessionRequest.GetAgentInfo + get_session_state: SessionRequest.GetSessionState + get_rtc_stats: SessionRequest.GetRTCStats + get_session_usage: SessionRequest.GetSessionUsage + get_framework_info: SessionRequest.GetFrameworkInfo + update_io: SessionRequest.UpdateIO + finalize_simulation: SessionRequest.FinalizeSimulation + def __init__(self, request_id: _Optional[str] = ..., ping: _Optional[_Union[SessionRequest.Ping, _Mapping]] = ..., get_chat_history: _Optional[_Union[SessionRequest.GetChatHistory, _Mapping]] = ..., run_input: _Optional[_Union[SessionRequest.RunInput, _Mapping]] = ..., get_agent_info: _Optional[_Union[SessionRequest.GetAgentInfo, _Mapping]] = ..., get_session_state: _Optional[_Union[SessionRequest.GetSessionState, _Mapping]] = ..., get_rtc_stats: _Optional[_Union[SessionRequest.GetRTCStats, _Mapping]] = ..., get_session_usage: _Optional[_Union[SessionRequest.GetSessionUsage, _Mapping]] = ..., get_framework_info: _Optional[_Union[SessionRequest.GetFrameworkInfo, _Mapping]] = ..., update_io: _Optional[_Union[SessionRequest.UpdateIO, _Mapping]] = ..., finalize_simulation: _Optional[_Union[SessionRequest.FinalizeSimulation, _Mapping]] = ...) -> None: ... + +class SessionResponse(_message.Message): + __slots__ = ("request_id", "error", "pong", "get_chat_history", "run_input", "get_agent_info", "get_session_state", "get_rtc_stats", "get_session_usage", "get_framework_info", "update_io", "finalize_simulation") + class Pong(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class GetChatHistoryResponse(_message.Message): + __slots__ = ("items",) + ITEMS_FIELD_NUMBER: _ClassVar[int] + items: _containers.RepeatedCompositeFieldContainer[ChatContext.ChatItem] + def __init__(self, items: _Optional[_Iterable[_Union[ChatContext.ChatItem, _Mapping]]] = ...) -> None: ... + class GetAgentInfoResponse(_message.Message): + __slots__ = ("id", "instructions", "tools", "chat_ctx") + ID_FIELD_NUMBER: _ClassVar[int] + INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int] + TOOLS_FIELD_NUMBER: _ClassVar[int] + CHAT_CTX_FIELD_NUMBER: _ClassVar[int] + id: str + instructions: str + tools: _containers.RepeatedScalarFieldContainer[str] + chat_ctx: _containers.RepeatedCompositeFieldContainer[ChatContext.ChatItem] + def __init__(self, id: _Optional[str] = ..., instructions: _Optional[str] = ..., tools: _Optional[_Iterable[str]] = ..., chat_ctx: _Optional[_Iterable[_Union[ChatContext.ChatItem, _Mapping]]] = ...) -> None: ... + class RunInputResponse(_message.Message): + __slots__ = ("items",) + ITEMS_FIELD_NUMBER: _ClassVar[int] + items: _containers.RepeatedCompositeFieldContainer[ChatContext.ChatItem] + def __init__(self, items: _Optional[_Iterable[_Union[ChatContext.ChatItem, _Mapping]]] = ...) -> None: ... + class GetSessionStateResponse(_message.Message): + __slots__ = ("agent_state", "user_state", "agent_id", "options", "created_at") + class OptionsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + AGENT_STATE_FIELD_NUMBER: _ClassVar[int] + USER_STATE_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + OPTIONS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + agent_state: AgentState + user_state: UserState + agent_id: str + options: _containers.ScalarMap[str, str] + created_at: _timestamp_pb2.Timestamp + def __init__(self, agent_state: _Optional[_Union[AgentState, str]] = ..., user_state: _Optional[_Union[UserState, str]] = ..., agent_id: _Optional[str] = ..., options: _Optional[_Mapping[str, str]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + class GetRTCStatsResponse(_message.Message): + __slots__ = ("publisher_stats", "subscriber_stats") + PUBLISHER_STATS_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBER_STATS_FIELD_NUMBER: _ClassVar[int] + publisher_stats: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + subscriber_stats: _containers.RepeatedCompositeFieldContainer[_struct_pb2.Struct] + def __init__(self, publisher_stats: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ..., subscriber_stats: _Optional[_Iterable[_Union[_struct_pb2.Struct, _Mapping]]] = ...) -> None: ... + class GetSessionUsageResponse(_message.Message): + __slots__ = ("usage", "created_at") + USAGE_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + usage: AgentSessionUsage + created_at: _timestamp_pb2.Timestamp + def __init__(self, usage: _Optional[_Union[AgentSessionUsage, _Mapping]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + class GetFrameworkInfoResponse(_message.Message): + __slots__ = ("sdk", "sdk_version") + SDK_FIELD_NUMBER: _ClassVar[int] + SDK_VERSION_FIELD_NUMBER: _ClassVar[int] + sdk: str + sdk_version: str + def __init__(self, sdk: _Optional[str] = ..., sdk_version: _Optional[str] = ...) -> None: ... + class UpdateIOResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class FinalizeSimulationResponse(_message.Message): + __slots__ = ("user_verdict",) + class SimulationVerdict(_message.Message): + __slots__ = ("success", "reason") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + success: bool + reason: str + def __init__(self, success: bool = ..., reason: _Optional[str] = ...) -> None: ... + USER_VERDICT_FIELD_NUMBER: _ClassVar[int] + user_verdict: SessionResponse.FinalizeSimulationResponse.SimulationVerdict + def __init__(self, user_verdict: _Optional[_Union[SessionResponse.FinalizeSimulationResponse.SimulationVerdict, _Mapping]] = ...) -> None: ... + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + PONG_FIELD_NUMBER: _ClassVar[int] + GET_CHAT_HISTORY_FIELD_NUMBER: _ClassVar[int] + RUN_INPUT_FIELD_NUMBER: _ClassVar[int] + GET_AGENT_INFO_FIELD_NUMBER: _ClassVar[int] + GET_SESSION_STATE_FIELD_NUMBER: _ClassVar[int] + GET_RTC_STATS_FIELD_NUMBER: _ClassVar[int] + GET_SESSION_USAGE_FIELD_NUMBER: _ClassVar[int] + GET_FRAMEWORK_INFO_FIELD_NUMBER: _ClassVar[int] + UPDATE_IO_FIELD_NUMBER: _ClassVar[int] + FINALIZE_SIMULATION_FIELD_NUMBER: _ClassVar[int] + request_id: str + error: str + pong: SessionResponse.Pong + get_chat_history: SessionResponse.GetChatHistoryResponse + run_input: SessionResponse.RunInputResponse + get_agent_info: SessionResponse.GetAgentInfoResponse + get_session_state: SessionResponse.GetSessionStateResponse + get_rtc_stats: SessionResponse.GetRTCStatsResponse + get_session_usage: SessionResponse.GetSessionUsageResponse + get_framework_info: SessionResponse.GetFrameworkInfoResponse + update_io: SessionResponse.UpdateIOResponse + finalize_simulation: SessionResponse.FinalizeSimulationResponse + def __init__(self, request_id: _Optional[str] = ..., error: _Optional[str] = ..., pong: _Optional[_Union[SessionResponse.Pong, _Mapping]] = ..., get_chat_history: _Optional[_Union[SessionResponse.GetChatHistoryResponse, _Mapping]] = ..., run_input: _Optional[_Union[SessionResponse.RunInputResponse, _Mapping]] = ..., get_agent_info: _Optional[_Union[SessionResponse.GetAgentInfoResponse, _Mapping]] = ..., get_session_state: _Optional[_Union[SessionResponse.GetSessionStateResponse, _Mapping]] = ..., get_rtc_stats: _Optional[_Union[SessionResponse.GetRTCStatsResponse, _Mapping]] = ..., get_session_usage: _Optional[_Union[SessionResponse.GetSessionUsageResponse, _Mapping]] = ..., get_framework_info: _Optional[_Union[SessionResponse.GetFrameworkInfoResponse, _Mapping]] = ..., update_io: _Optional[_Union[SessionResponse.UpdateIOResponse, _Mapping]] = ..., finalize_simulation: _Optional[_Union[SessionResponse.FinalizeSimulationResponse, _Mapping]] = ...) -> None: ... + +class AgentSessionMessage(_message.Message): + __slots__ = ("audio_input", "audio_output", "event", "request", "response", "audio_playback_flush", "audio_playback_clear", "audio_playback_finished") + class ConsoleIO(_message.Message): + __slots__ = () + class AudioFrame(_message.Message): + __slots__ = ("data", "sample_rate", "num_channels", "samples_per_channel") + DATA_FIELD_NUMBER: _ClassVar[int] + SAMPLE_RATE_FIELD_NUMBER: _ClassVar[int] + NUM_CHANNELS_FIELD_NUMBER: _ClassVar[int] + SAMPLES_PER_CHANNEL_FIELD_NUMBER: _ClassVar[int] + data: bytes + sample_rate: int + num_channels: int + samples_per_channel: int + def __init__(self, data: _Optional[bytes] = ..., sample_rate: _Optional[int] = ..., num_channels: _Optional[int] = ..., samples_per_channel: _Optional[int] = ...) -> None: ... + class AudioPlaybackFlush(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class AudioPlaybackClear(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + class AudioPlaybackFinished(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + def __init__(self) -> None: ... + AUDIO_INPUT_FIELD_NUMBER: _ClassVar[int] + AUDIO_OUTPUT_FIELD_NUMBER: _ClassVar[int] + EVENT_FIELD_NUMBER: _ClassVar[int] + REQUEST_FIELD_NUMBER: _ClassVar[int] + RESPONSE_FIELD_NUMBER: _ClassVar[int] + AUDIO_PLAYBACK_FLUSH_FIELD_NUMBER: _ClassVar[int] + AUDIO_PLAYBACK_CLEAR_FIELD_NUMBER: _ClassVar[int] + AUDIO_PLAYBACK_FINISHED_FIELD_NUMBER: _ClassVar[int] + audio_input: AgentSessionMessage.ConsoleIO.AudioFrame + audio_output: AgentSessionMessage.ConsoleIO.AudioFrame + event: AgentSessionEvent + request: SessionRequest + response: SessionResponse + audio_playback_flush: AgentSessionMessage.ConsoleIO.AudioPlaybackFlush + audio_playback_clear: AgentSessionMessage.ConsoleIO.AudioPlaybackClear + audio_playback_finished: AgentSessionMessage.ConsoleIO.AudioPlaybackFinished + def __init__(self, audio_input: _Optional[_Union[AgentSessionMessage.ConsoleIO.AudioFrame, _Mapping]] = ..., audio_output: _Optional[_Union[AgentSessionMessage.ConsoleIO.AudioFrame, _Mapping]] = ..., event: _Optional[_Union[AgentSessionEvent, _Mapping]] = ..., request: _Optional[_Union[SessionRequest, _Mapping]] = ..., response: _Optional[_Union[SessionResponse, _Mapping]] = ..., audio_playback_flush: _Optional[_Union[AgentSessionMessage.ConsoleIO.AudioPlaybackFlush, _Mapping]] = ..., audio_playback_clear: _Optional[_Union[AgentSessionMessage.ConsoleIO.AudioPlaybackClear, _Mapping]] = ..., audio_playback_finished: _Optional[_Union[AgentSessionMessage.ConsoleIO.AudioPlaybackFinished, _Mapping]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_text.py b/livekit-protocol/livekit/protocol/agent_pb/agent_text.py new file mode 100644 index 00000000..f6d86f64 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_text.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: agent/livekit_agent_text.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'agent/livekit_agent_text.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import agent_session as agent_dot__agent__session_ +from ..logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1e\x61gent/livekit_agent_text.proto\x12\rlivekit.agent\x1a!agent/livekit_agent_session.proto\x1a\x14logger/options.proto\"\xdc\x01\n\x12TextMessageRequest\x12\x0c\n\x04text\x18\x01 \x01(\t\x12 \n\nmessage_id\x18\x02 \x01(\tB\x0c\xbaP\tmessageID\x12 \n\nsession_id\x18\x03 \x01(\tB\x0c\xbaP\tsessionID\x12\x12\n\nagent_name\x18\x04 \x01(\t\x12\x10\n\x08metadata\x18\x05 \x01(\t\x12<\n\rsession_state\x18\x06 \x01(\x0b\x32 .livekit.agent.AgentSessionStateH\x00\x88\x01\x01\x42\x10\n\x0e_session_state\"\xf8\x02\n\x13TextMessageResponse\x12 \n\nmessage_id\x18\x01 \x01(\tB\x0c\xbaP\tmessageID\x12 \n\nsession_id\x18\x02 \x01(\tB\x0c\xbaP\tsessionID\x12-\n\x07message\x18\x03 \x01(\x0b\x32\x1a.livekit.agent.ChatMessageH\x00\x12\x34\n\rfunction_call\x18\x04 \x01(\x0b\x32\x1b.livekit.agent.FunctionCallH\x00\x12\x41\n\x14\x66unction_call_output\x18\x05 \x01(\x0b\x32!.livekit.agent.FunctionCallOutputH\x00\x12\x34\n\ragent_handoff\x18\x06 \x01(\x0b\x32\x1b.livekit.agent.AgentHandoffH\x00\x12\x36\n\x08\x63omplete\x18\x07 \x01(\x0b\x32\".livekit.agent.TextMessageCompleteH\x00\x42\x07\n\x05\x65vent\"\x8c\x01\n\x13TextMessageComplete\x12\x39\n\rsession_state\x18\x01 \x01(\x0b\x32 .livekit.agent.AgentSessionStateH\x00\x12\x30\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1f.livekit.agent.TextMessageErrorH\x00\x42\x08\n\x06result\"Q\n\x11\x41gentSessionState\x12\x0f\n\x07version\x18\x01 \x01(\x04\x12\x12\n\x08snapshot\x18\x02 \x01(\x0cH\x00\x12\x0f\n\x05\x64\x65lta\x18\x03 \x01(\x0cH\x00\x42\x06\n\x04\x64\x61ta\"V\n\x10TextMessageError\x12\x0f\n\x07message\x18\x01 \x01(\t\x12\x31\n\x04\x63ode\x18\x02 \x01(\x0e\x32#.livekit.agent.TextMessageErrorCode*s\n\x14TextMessageErrorCode\x12\x12\n\x0eINTERNAL_ERROR\x10\x00\x12\x1b\n\x17SESSION_STATE_NOT_FOUND\x10\x01\x12\x16\n\x12TEXT_HANDLER_ERROR\x10\x02\x12\x12\n\x0ePROCESS_CLOSED\x10\x03\x42LZ)github.com/livekit/protocol/livekit/agent\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent.agent_text', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z)github.com/livekit/protocol/livekit/agent\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_TEXTMESSAGEREQUEST'].fields_by_name['message_id']._loaded_options = None + _globals['_TEXTMESSAGEREQUEST'].fields_by_name['message_id']._serialized_options = b'\272P\tmessageID' + _globals['_TEXTMESSAGEREQUEST'].fields_by_name['session_id']._loaded_options = None + _globals['_TEXTMESSAGEREQUEST'].fields_by_name['session_id']._serialized_options = b'\272P\tsessionID' + _globals['_TEXTMESSAGERESPONSE'].fields_by_name['message_id']._loaded_options = None + _globals['_TEXTMESSAGERESPONSE'].fields_by_name['message_id']._serialized_options = b'\272P\tmessageID' + _globals['_TEXTMESSAGERESPONSE'].fields_by_name['session_id']._loaded_options = None + _globals['_TEXTMESSAGERESPONSE'].fields_by_name['session_id']._serialized_options = b'\272P\tsessionID' + _globals['_TEXTMESSAGEERRORCODE']._serialized_start=1022 + _globals['_TEXTMESSAGEERRORCODE']._serialized_end=1137 + _globals['_TEXTMESSAGEREQUEST']._serialized_start=107 + _globals['_TEXTMESSAGEREQUEST']._serialized_end=327 + _globals['_TEXTMESSAGERESPONSE']._serialized_start=330 + _globals['_TEXTMESSAGERESPONSE']._serialized_end=706 + _globals['_TEXTMESSAGECOMPLETE']._serialized_start=709 + _globals['_TEXTMESSAGECOMPLETE']._serialized_end=849 + _globals['_AGENTSESSIONSTATE']._serialized_start=851 + _globals['_AGENTSESSIONSTATE']._serialized_end=932 + _globals['_TEXTMESSAGEERROR']._serialized_start=934 + _globals['_TEXTMESSAGEERROR']._serialized_end=1020 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_pb/agent_text.pyi b/livekit-protocol/livekit/protocol/agent_pb/agent_text.pyi new file mode 100644 index 00000000..2f2199f0 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_pb/agent_text.pyi @@ -0,0 +1,79 @@ +from . import agent_session as _agent_session +from ..logger_pb import options as _options_pb2 +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class TextMessageErrorCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + INTERNAL_ERROR: _ClassVar[TextMessageErrorCode] + SESSION_STATE_NOT_FOUND: _ClassVar[TextMessageErrorCode] + TEXT_HANDLER_ERROR: _ClassVar[TextMessageErrorCode] + PROCESS_CLOSED: _ClassVar[TextMessageErrorCode] +INTERNAL_ERROR: TextMessageErrorCode +SESSION_STATE_NOT_FOUND: TextMessageErrorCode +TEXT_HANDLER_ERROR: TextMessageErrorCode +PROCESS_CLOSED: TextMessageErrorCode + +class TextMessageRequest(_message.Message): + __slots__ = ("text", "message_id", "session_id", "agent_name", "metadata", "session_state") + TEXT_FIELD_NUMBER: _ClassVar[int] + MESSAGE_ID_FIELD_NUMBER: _ClassVar[int] + SESSION_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + SESSION_STATE_FIELD_NUMBER: _ClassVar[int] + text: str + message_id: str + session_id: str + agent_name: str + metadata: str + session_state: AgentSessionState + def __init__(self, text: _Optional[str] = ..., message_id: _Optional[str] = ..., session_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., metadata: _Optional[str] = ..., session_state: _Optional[_Union[AgentSessionState, _Mapping]] = ...) -> None: ... + +class TextMessageResponse(_message.Message): + __slots__ = ("message_id", "session_id", "message", "function_call", "function_call_output", "agent_handoff", "complete") + MESSAGE_ID_FIELD_NUMBER: _ClassVar[int] + SESSION_ID_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + FUNCTION_CALL_FIELD_NUMBER: _ClassVar[int] + FUNCTION_CALL_OUTPUT_FIELD_NUMBER: _ClassVar[int] + AGENT_HANDOFF_FIELD_NUMBER: _ClassVar[int] + COMPLETE_FIELD_NUMBER: _ClassVar[int] + message_id: str + session_id: str + message: _agent_session.ChatMessage + function_call: _agent_session.FunctionCall + function_call_output: _agent_session.FunctionCallOutput + agent_handoff: _agent_session.AgentHandoff + complete: TextMessageComplete + def __init__(self, message_id: _Optional[str] = ..., session_id: _Optional[str] = ..., message: _Optional[_Union[_agent_session.ChatMessage, _Mapping]] = ..., function_call: _Optional[_Union[_agent_session.FunctionCall, _Mapping]] = ..., function_call_output: _Optional[_Union[_agent_session.FunctionCallOutput, _Mapping]] = ..., agent_handoff: _Optional[_Union[_agent_session.AgentHandoff, _Mapping]] = ..., complete: _Optional[_Union[TextMessageComplete, _Mapping]] = ...) -> None: ... + +class TextMessageComplete(_message.Message): + __slots__ = ("session_state", "error") + SESSION_STATE_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + session_state: AgentSessionState + error: TextMessageError + def __init__(self, session_state: _Optional[_Union[AgentSessionState, _Mapping]] = ..., error: _Optional[_Union[TextMessageError, _Mapping]] = ...) -> None: ... + +class AgentSessionState(_message.Message): + __slots__ = ("version", "snapshot", "delta") + VERSION_FIELD_NUMBER: _ClassVar[int] + SNAPSHOT_FIELD_NUMBER: _ClassVar[int] + DELTA_FIELD_NUMBER: _ClassVar[int] + version: int + snapshot: bytes + delta: bytes + def __init__(self, version: _Optional[int] = ..., snapshot: _Optional[bytes] = ..., delta: _Optional[bytes] = ...) -> None: ... + +class TextMessageError(_message.Message): + __slots__ = ("message", "code") + MESSAGE_FIELD_NUMBER: _ClassVar[int] + CODE_FIELD_NUMBER: _ClassVar[int] + message: str + code: TextMessageErrorCode + def __init__(self, message: _Optional[str] = ..., code: _Optional[_Union[TextMessageErrorCode, str]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_simulation.py b/livekit-protocol/livekit/protocol/agent_simulation.py new file mode 100644 index 00000000..58ff8795 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_simulation.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_agent_simulation.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_agent_simulation.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from .agent_pb import agent_session as agent_dot__agent__session_ +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from . import cloud_agent as _cloud__agent_ +from . import models as _models_ + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1elivekit_agent_simulation.proto\x12\x07livekit\x1a!agent/livekit_agent_session.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x19livekit_cloud_agent.proto\x1a\x14livekit_models.proto\"\xea\x02\n\x14SimulationRunSummary\x12\x0e\n\x06passed\x18\x01 \x01(\x05\x12\x0e\n\x06\x66\x61iled\x18\x02 \x01(\x05\x12\x12\n\ngoing_well\x18\x03 \x01(\t\x12\x12\n\nto_improve\x18\x04 \x01(\t\x12\x33\n\x06issues\x18\x05 \x03(\x0b\x32#.livekit.SimulationRunSummary.Issue\x12\x44\n\x0c\x63hat_history\x18\x06 \x03(\x0b\x32..livekit.SimulationRunSummary.ChatHistoryEntry\x1aN\n\x10\x43hatHistoryEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12)\n\x05value\x18\x02 \x01(\x0b\x32\x1a.livekit.agent.ChatContext:\x02\x38\x01\x1a?\n\x05Issue\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x12\n\nsuggestion\x18\x02 \x01(\t\x12\r\n\x05label\x18\x03 \x01(\t\"\xf3\x32\n\rSimulationRun\x12\n\n\x02id\x18\x01 \x01(\t\x12\x12\n\nproject_id\x18\x02 \x01(\t\x12-\n\x06status\x18\x03 \x01(\x0e\x32\x1d.livekit.SimulationRun.Status\x12\x19\n\x11\x61gent_description\x18\x04 \x01(\t\x12\r\n\x05\x65rror\x18\x05 \x01(\t\x12.\n\ncreated_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12(\n\x04jobs\x18\x07 \x03(\x0b\x32\x1a.livekit.SimulationRun.Job\x12\x12\n\nagent_name\x18\t \x01(\t\x12.\n\x0escenario_group\x18\n \x01(\x0b\x32\x16.livekit.ScenarioGroup\x12,\n\x08\x65nded_at\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x11\n\tjob_count\x18\x0c \x01(\x05\x12\x14\n\x0cpassed_count\x18\r \x01(\x05\x12\x14\n\x0c\x66\x61iled_count\x18\x0e \x01(\x05\x12\x17\n\x0fnum_simulations\x18\x0f \x01(\x05\x12+\n\x05usage\x18\x10 \x01(\x0b\x32\x1c.livekit.SimulationRun.Usage\x12\x13\n\x0b\x63oncurrency\x18\x11 \x01(\x05\x12%\n\x04mode\x18\x12 \x01(\x0e\x32\x17.livekit.SimulationMode\x12\x32\n\x07metrics\x18\x13 \x01(\x0b\x32!.livekit.SimulationRun.RunMetrics\x12\x14\n\x0csummary_zstd\x18\x14 \x01(\x0c\x1a\xbe\x04\n\x03Job\x12\n\n\x02id\x18\x01 \x01(\t\x12\x31\n\x06status\x18\x02 \x01(\x0e\x32!.livekit.SimulationRun.Job.Status\x12\x14\n\x0cinstructions\x18\x03 \x01(\t\x12\r\n\x05\x65rror\x18\x04 \x01(\t\x12\x1a\n\x12\x61gent_expectations\x18\x05 \x01(\t\x12\r\n\x05label\x18\x06 \x01(\t\x12\x0c\n\x04tags\x18\x07 \x03(\t\x12\x11\n\troom_name\x18\x08 \x01(\t\x12.\n\nstarted_at\x18\n \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nded_at\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07room_id\x18\x0c \x01(\t\x12/\n\x05usage\x18\r \x01(\x0b\x32 .livekit.SimulationRun.Job.Usage\x12\x32\n\x07metrics\x18\x0e \x01(\x0b\x32!.livekit.SimulationRun.JobMetrics\x1a<\n\x05Usage\x12\x18\n\x10text_turns_count\x18\x01 \x01(\x05\x12\x19\n\x11\x61udio_turns_count\x18\x02 \x01(\x05\"o\n\x06Status\x12\x12\n\x0eSTATUS_PENDING\x10\x00\x12\x12\n\x0eSTATUS_RUNNING\x10\x01\x12\x14\n\x10STATUS_COMPLETED\x10\x02\x12\x11\n\rSTATUS_FAILED\x10\x03\x12\x14\n\x10STATUS_CANCELLED\x10\x04J\x04\x08\t\x10\n\x1a\x87\x1d\n\nJobMetrics\x12\x1b\n\x0e\x61\x63\x63uracy_score\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x1d\n\x10\x65xperience_score\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12\x1c\n\x0ftask_completion\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x32\n\x03stt\x18\x04 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.STT\x12\x32\n\x03llm\x18\x05 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.LLM\x12\x32\n\x03tts\x18\x06 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.TTS\x12\x44\n\x0c\x63onversation\x18\x07 \x01(\x0b\x32..livekit.SimulationRun.JobMetrics.Conversation\x12\x35\n\x05turns\x18\t \x03(\x0b\x32&.livekit.SimulationRun.JobMetrics.Turn\x12\x13\n\x0bjudge_model\x18\n \x01(\t\x12\x1a\n\x12has_remote_session\x18\x0c \x01(\x08\x12&\n\x02t0\x18\r \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x18\n\x0b\x63onciseness\x18\x0e \x01(\x02H\x03\x88\x01\x01\x12#\n\x16unnecessary_tool_calls\x18\x0f \x01(\x08H\x04\x88\x01\x01\x12\x1d\n\x10information_loss\x18\x10 \x01(\x08H\x05\x88\x01\x01\x12!\n\x14redundant_statements\x18\x11 \x01(\x08H\x06\x88\x01\x01\x12\"\n\x15poor_question_quality\x18\x12 \x01(\x08H\x07\x88\x01\x01\x12%\n\x18\x63onversation_progression\x18\x13 \x01(\x02H\x08\x88\x01\x01\x1a\xdb\x04\n\x03STT\x12\x10\n\x03wer\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x12\n\x05words\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x18\n\x0bword_errors\x18\x03 \x01(\rH\x02\x88\x01\x01\x12\x10\n\x03\x63\x65r\x18\x04 \x01(\x02H\x03\x88\x01\x01\x12\x12\n\x05\x63hars\x18\t \x01(\rH\x04\x88\x01\x01\x12\x18\n\x0b\x63har_errors\x18\n \x01(\rH\x05\x88\x01\x01\x12\x19\n\x0cstt_delay_ms\x18\x08 \x01(\rH\x06\x88\x01\x01\x12\x1f\n\x12\x65ntity_recognition\x18\x0b \x01(\x02H\x07\x88\x01\x01\x12\x1d\n\x10\x65ntities_uttered\x18\x0c \x01(\rH\x08\x88\x01\x01\x12 \n\x13\x65ntities_recognized\x18\r \x01(\rH\t\x88\x01\x01\x12\x1a\n\rentity_recall\x18\x0e \x01(\x02H\n\x88\x01\x01\x12&\n\x19\x65ntities_acquired_uttered\x18\x0f \x01(\rH\x0b\x88\x01\x01\x12)\n\x1c\x65ntities_acquired_recognized\x18\x10 \x01(\rH\x0c\x88\x01\x01\x42\x06\n\x04_werB\x08\n\x06_wordsB\x0e\n\x0c_word_errorsB\x06\n\x04_cerB\x08\n\x06_charsB\x0e\n\x0c_char_errorsB\x0f\n\r_stt_delay_msB\x15\n\x13_entity_recognitionB\x13\n\x11_entities_utteredB\x16\n\x14_entities_recognizedB\x10\n\x0e_entity_recallB\x1c\n\x1a_entities_acquired_utteredB\x1f\n\x1d_entities_acquired_recognized\x1a\x7f\n\x03LLM\x12\x14\n\x07ttft_ms\x18\x01 \x01(\rH\x00\x88\x01\x01\x12\x14\n\x07ttfs_ms\x18\x02 \x01(\rH\x01\x88\x01\x01\x12\x1e\n\x11tokens_per_second\x18\x03 \x01(\x02H\x02\x88\x01\x01\x42\n\n\x08_ttft_msB\n\n\x08_ttfs_msB\x14\n\x12_tokens_per_second\x1a\xd1\x04\n\x03TTS\x12\x14\n\x07ttfb_ms\x18\x02 \x01(\rH\x00\x88\x01\x01\x12\x10\n\x03wer\x18\x03 \x01(\x02H\x01\x88\x01\x01\x12\x12\n\x05words\x18\x07 \x01(\rH\x02\x88\x01\x01\x12\x18\n\x0bword_errors\x18\x08 \x01(\rH\x03\x88\x01\x01\x12\x10\n\x03\x63\x65r\x18\t \x01(\x02H\x04\x88\x01\x01\x12\x12\n\x05\x63hars\x18\n \x01(\rH\x05\x88\x01\x01\x12\x18\n\x0b\x63har_errors\x18\x0b \x01(\rH\x06\x88\x01\x01\x12\x1f\n\x12\x65ntity_recognition\x18\x0e \x01(\x02H\x07\x88\x01\x01\x12\x1d\n\x10\x65ntities_uttered\x18\x0f \x01(\rH\x08\x88\x01\x01\x12 \n\x13\x65ntities_recognized\x18\x10 \x01(\rH\t\x88\x01\x01\x12\x1a\n\rentity_recall\x18\x11 \x01(\x02H\n\x88\x01\x01\x12&\n\x19\x65ntities_acquired_uttered\x18\x12 \x01(\rH\x0b\x88\x01\x01\x12)\n\x1c\x65ntities_acquired_recognized\x18\x13 \x01(\rH\x0c\x88\x01\x01\x42\n\n\x08_ttfb_msB\x06\n\x04_werB\x08\n\x06_wordsB\x0e\n\x0c_word_errorsB\x06\n\x04_cerB\x08\n\x06_charsB\x0e\n\x0c_char_errorsB\x15\n\x13_entity_recognitionB\x13\n\x11_entities_utteredB\x16\n\x14_entities_recognizedB\x10\n\x0e_entity_recallB\x1c\n\x1a_entities_acquired_utteredB\x1f\n\x1d_entities_acquired_recognized\x1a\x82\x06\n\x0c\x43onversation\x12\x1e\n\x11turn_taking_score\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12%\n\x18heard_e2e_latency_p50_ms\x18\x02 \x01(\x05H\x01\x88\x01\x01\x12%\n\x18heard_e2e_latency_p95_ms\x18\x03 \x01(\x05H\x02\x88\x01\x01\x12%\n\x18heard_e2e_latency_p99_ms\x18\x04 \x01(\x05H\x03\x88\x01\x01\x12\x1d\n\x10time_to_yield_ms\x18\x05 \x01(\rH\x04\x88\x01\x01\x12$\n\x17\x65ot_misprediction_count\x18\x06 \x01(\rH\x05\x88\x01\x01\x12\x1a\n\roverlap_ratio\x18\x07 \x01(\x02H\x06\x88\x01\x01\x12\x1e\n\x11overlap_speech_ms\x18\x08 \x01(\rH\x07\x88\x01\x01\x12\x1c\n\x0ftotal_speech_ms\x18\t \x01(\rH\x08\x88\x01\x01\x12\"\n\x15\x61wkward_silence_count\x18\x0b \x01(\rH\t\x88\x01\x01\x12\x1d\n\x10unanswered_turns\x18\x0c \x01(\rH\n\x88\x01\x01\x12%\n\x18\x66\x61lse_interruption_count\x18\r \x01(\rH\x0b\x88\x01\x01\x12\x1b\n\x0e\x65\x32\x65_latency_ms\x18\x0f \x01(\rH\x0c\x88\x01\x01\x42\x14\n\x12_turn_taking_scoreB\x1b\n\x19_heard_e2e_latency_p50_msB\x1b\n\x19_heard_e2e_latency_p95_msB\x1b\n\x19_heard_e2e_latency_p99_msB\x13\n\x11_time_to_yield_msB\x1a\n\x18_eot_misprediction_countB\x10\n\x0e_overlap_ratioB\x14\n\x12_overlap_speech_msB\x12\n\x10_total_speech_msB\x18\n\x16_awkward_silence_countB\x13\n\x11_unanswered_turnsB\x1b\n\x19_false_interruption_countB\x11\n\x0f_e2e_latency_ms\x1a\xdc\x05\n\x04Turn\x12\r\n\x05index\x18\x01 \x01(\r\x12%\n\x04role\x18\x02 \x01(\x0e\x32\x17.livekit.agent.ChatRole\x12\x15\n\x08start_ms\x18\x03 \x01(\rH\x00\x88\x01\x01\x12\x13\n\x06\x65nd_ms\x18\x04 \x01(\rH\x01\x88\x01\x01\x12\x19\n\x0cstt_delay_ms\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x18\n\x0bllm_ttft_ms\x18\x06 \x01(\rH\x03\x88\x01\x01\x12\x18\n\x0bllm_ttfs_ms\x18\x07 \x01(\rH\x04\x88\x01\x01\x12\x14\n\x07llm_tps\x18\x08 \x01(\x02H\x05\x88\x01\x01\x12\x18\n\x0btts_ttfb_ms\x18\t \x01(\rH\x06\x88\x01\x01\x12\x1b\n\x0e\x65\x32\x65_latency_ms\x18\n \x01(\rH\x07\x88\x01\x01\x12!\n\x14heard_e2e_latency_ms\x18\x0b \x01(\x05H\x08\x88\x01\x01\x12\x1d\n\x10time_to_yield_ms\x18\x0c \x01(\rH\t\x88\x01\x01\x12\x1e\n\x11turn_taking_score\x18\r \x01(\x02H\n\x88\x01\x01\x12\x18\n\x0b\x63onciseness\x18\x0e \x01(\x02H\x0b\x88\x01\x01\x12\x1e\n\x11\x65ot_misprediction\x18\x0f \x01(\x08H\x0c\x88\x01\x01\x12\x1c\n\x0f\x61wkward_silence\x18\x10 \x01(\x08H\r\x88\x01\x01\x12\x17\n\nunanswered\x18\x11 \x01(\x08H\x0e\x88\x01\x01\x42\x0b\n\t_start_msB\t\n\x07_end_msB\x0f\n\r_stt_delay_msB\x0e\n\x0c_llm_ttft_msB\x0e\n\x0c_llm_ttfs_msB\n\n\x08_llm_tpsB\x0e\n\x0c_tts_ttfb_msB\x11\n\x0f_e2e_latency_msB\x17\n\x15_heard_e2e_latency_msB\x13\n\x11_time_to_yield_msB\x14\n\x12_turn_taking_scoreB\x0e\n\x0c_concisenessB\x14\n\x12_eot_mispredictionB\x12\n\x10_awkward_silenceB\r\n\x0b_unansweredB\x11\n\x0f_accuracy_scoreB\x13\n\x11_experience_scoreB\x12\n\x10_task_completionB\x0e\n\x0c_concisenessB\x19\n\x17_unnecessary_tool_callsB\x13\n\x11_information_lossB\x17\n\x15_redundant_statementsB\x18\n\x16_poor_question_qualityB\x1b\n\x19_conversation_progression\x1a\x8a\x03\n\nRunMetrics\x12\x1b\n\x0e\x61\x63\x63uracy_score\x18\x01 \x01(\x02H\x00\x88\x01\x01\x12\x1d\n\x10\x65xperience_score\x18\x02 \x01(\x02H\x01\x88\x01\x01\x12\x1f\n\x12scenario_pass_rate\x18\x03 \x01(\x02H\x02\x88\x01\x01\x12\x32\n\x03stt\x18\x04 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.STT\x12\x32\n\x03llm\x18\x05 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.LLM\x12\x32\n\x03tts\x18\x06 \x01(\x0b\x32%.livekit.SimulationRun.JobMetrics.TTS\x12\x44\n\x0c\x63onversation\x18\x07 \x01(\x0b\x32..livekit.SimulationRun.JobMetrics.ConversationB\x11\n\x0f_accuracy_scoreB\x13\n\x11_experience_scoreB\x15\n\x13_scenario_pass_rate\x1a\xfd\x02\n\x06\x43reate\x1a\x8c\x02\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x17\n\x0fnum_simulations\x18\x04 \x01(\x05\x12\x0e\n\x06region\x18\x06 \x01(\t\x12\x33\n\x0escenario_group\x18\x07 \x01(\x0b\x32\x16.livekit.ScenarioGroupH\x00\x88\x01\x01\x12\x18\n\x0b\x63oncurrency\x18\x08 \x01(\x05H\x01\x88\x01\x01\x12%\n\x04mode\x18\t \x01(\x0e\x32\x17.livekit.SimulationModeB\x11\n\x0f_scenario_groupB\x0e\n\x0c_concurrencyJ\x04\x08\x03\x10\x04R\x11\x61gent_description\x1a\x64\n\x08Response\x12\x19\n\x11simulation_run_id\x18\x01 \x01(\t\x12=\n\x16presigned_post_request\x18\x02 \x01(\x0b\x32\x1d.livekit.PresignedPostRequest\x1at\n\x13\x43onfirmSourceUpload\x1aQ\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x19\n\x11simulation_run_id\x18\x02 \x01(\t\x12\x17\n\x0f\x63ode_entrypoint\x18\x03 \x01(\t\x1a\n\n\x08Response\x1ap\n\x03Get\x1a\x38\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x19\n\x11simulation_run_id\x18\x02 \x01(\t\x1a/\n\x08Response\x12#\n\x03run\x18\x01 \x01(\x0b\x32\x16.livekit.SimulationRun\x1a\x8c\x02\n\x04List\x1a\x9e\x01\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x32\n\x06status\x18\x02 \x01(\x0e\x32\x1d.livekit.SimulationRun.StatusH\x00\x88\x01\x01\x12\x31\n\npage_token\x18\x03 \x01(\x0b\x32\x18.livekit.TokenPaginationH\x01\x88\x01\x01\x42\t\n\x07_statusB\r\n\x0b_page_token\x1a\x63\n\x08Response\x12$\n\x04runs\x18\x01 \x03(\x0b\x32\x16.livekit.SimulationRun\x12\x31\n\x0fnext_page_token\x18\x02 \x01(\x0b\x32\x18.livekit.TokenPagination\x1aN\n\x06\x43\x61ncel\x1a\x38\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x19\n\x11simulation_run_id\x18\x02 \x01(\t\x1a\n\n\x08Response\x1a<\n\x05Usage\x12\x18\n\x10text_turns_count\x18\x01 \x01(\x05\x12\x19\n\x11\x61udio_turns_count\x18\x02 \x01(\x05\"\xa5\x01\n\x06Status\x12\x19\n\x15STATUS_PENDING_UPLOAD\x10\x00\x12\x15\n\x11STATUS_GENERATING\x10\x01\x12\x12\n\x0eSTATUS_RUNNING\x10\x02\x12\x16\n\x12STATUS_SUMMARIZING\x10\x03\x12\x14\n\x10STATUS_COMPLETED\x10\x04\x12\x11\n\rSTATUS_FAILED\x10\x05\x12\x14\n\x10STATUS_CANCELLED\x10\x06J\x04\x08\x08\x10\tR\x07summary\"\xbc\x02\n\x08Scenario\x12\r\n\x05label\x18\x01 \x01(\t\x12\x14\n\x0cinstructions\x18\x02 \x01(\t\x12\x1a\n\x12\x61gent_expectations\x18\x03 \x01(\t\x12)\n\x04tags\x18\x04 \x03(\x0b\x32\x1b.livekit.Scenario.TagsEntry\x12\x10\n\x08userdata\x18\x05 \x01(\t\x1a\x84\x01\n\x11\x43reateFromSession\x1a>\n\x07Request\x12\x12\n\nproject_id\x18\x01 \x01(\t\x12\x0f\n\x07room_id\x18\x02 \x01(\t\x12\x0e\n\x06region\x18\x03 \x01(\t\x1a/\n\x08Response\x12#\n\x08scenario\x18\x01 \x01(\x0b\x32\x11.livekit.Scenario\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"C\n\rScenarioGroup\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\tscenarios\x18\x02 \x03(\x0b\x32\x11.livekit.Scenario\"\x8b\x01\n\x12SimulationDispatch\x12\x19\n\x11simulation_run_id\x18\x01 \x01(\t\x12\x0e\n\x06job_id\x18\x02 \x01(\t\x12#\n\x08scenario\x18\x03 \x01(\x0b\x32\x11.livekit.Scenario\x12%\n\x04mode\x18\x04 \x01(\x0e\x32\x17.livekit.SimulationMode*f\n\x0eSimulationMode\x12\x1f\n\x1bSIMULATION_MODE_UNSPECIFIED\x10\x00\x12\x18\n\x14SIMULATION_MODE_TEXT\x10\x01\x12\x19\n\x15SIMULATION_MODE_AUDIO\x10\x02\x32\x9e\x05\n\x0f\x41gentSimulation\x12\x64\n\x13\x43reateSimulationRun\x12%.livekit.SimulationRun.Create.Request\x1a&.livekit.SimulationRun.Create.Response\x12\x88\x01\n\x1d\x43onfirmSimulationSourceUpload\x12\x32.livekit.SimulationRun.ConfirmSourceUpload.Request\x1a\x33.livekit.SimulationRun.ConfirmSourceUpload.Response\x12[\n\x10GetSimulationRun\x12\".livekit.SimulationRun.Get.Request\x1a#.livekit.SimulationRun.Get.Response\x12_\n\x12ListSimulationRuns\x12#.livekit.SimulationRun.List.Request\x1a$.livekit.SimulationRun.List.Response\x12\x64\n\x13\x43\x61ncelSimulationRun\x12%.livekit.SimulationRun.Cancel.Request\x1a&.livekit.SimulationRun.Cancel.Response\x12v\n\x19\x43reateScenarioFromSession\x12+.livekit.Scenario.CreateFromSession.Request\x1a,.livekit.Scenario.CreateFromSession.ResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_simulation', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_SIMULATIONRUNSUMMARY_CHATHISTORYENTRY']._loaded_options = None + _globals['_SIMULATIONRUNSUMMARY_CHATHISTORYENTRY']._serialized_options = b'8\001' + _globals['_SCENARIO_TAGSENTRY']._loaded_options = None + _globals['_SCENARIO_TAGSENTRY']._serialized_options = b'8\001' + _globals['_SIMULATIONMODE']._serialized_start=7573 + _globals['_SIMULATIONMODE']._serialized_end=7675 + _globals['_SIMULATIONRUNSUMMARY']._serialized_start=161 + _globals['_SIMULATIONRUNSUMMARY']._serialized_end=523 + _globals['_SIMULATIONRUNSUMMARY_CHATHISTORYENTRY']._serialized_start=380 + _globals['_SIMULATIONRUNSUMMARY_CHATHISTORYENTRY']._serialized_end=458 + _globals['_SIMULATIONRUNSUMMARY_ISSUE']._serialized_start=460 + _globals['_SIMULATIONRUNSUMMARY_ISSUE']._serialized_end=523 + _globals['_SIMULATIONRUN']._serialized_start=526 + _globals['_SIMULATIONRUN']._serialized_end=7041 + _globals['_SIMULATIONRUN_JOB']._serialized_start=1136 + _globals['_SIMULATIONRUN_JOB']._serialized_end=1710 + _globals['_SIMULATIONRUN_JOB_USAGE']._serialized_start=1531 + _globals['_SIMULATIONRUN_JOB_USAGE']._serialized_end=1591 + _globals['_SIMULATIONRUN_JOB_STATUS']._serialized_start=1593 + _globals['_SIMULATIONRUN_JOB_STATUS']._serialized_end=1704 + _globals['_SIMULATIONRUN_JOBMETRICS']._serialized_start=1713 + _globals['_SIMULATIONRUN_JOBMETRICS']._serialized_end=5432 + _globals['_SIMULATIONRUN_JOBMETRICS_STT']._serialized_start=2392 + _globals['_SIMULATIONRUN_JOBMETRICS_STT']._serialized_end=2995 + _globals['_SIMULATIONRUN_JOBMETRICS_LLM']._serialized_start=2997 + _globals['_SIMULATIONRUN_JOBMETRICS_LLM']._serialized_end=3124 + _globals['_SIMULATIONRUN_JOBMETRICS_TTS']._serialized_start=3127 + _globals['_SIMULATIONRUN_JOBMETRICS_TTS']._serialized_end=3720 + _globals['_SIMULATIONRUN_JOBMETRICS_CONVERSATION']._serialized_start=3723 + _globals['_SIMULATIONRUN_JOBMETRICS_CONVERSATION']._serialized_end=4493 + _globals['_SIMULATIONRUN_JOBMETRICS_TURN']._serialized_start=4496 + _globals['_SIMULATIONRUN_JOBMETRICS_TURN']._serialized_end=5228 + _globals['_SIMULATIONRUN_RUNMETRICS']._serialized_start=5435 + _globals['_SIMULATIONRUN_RUNMETRICS']._serialized_end=5829 + _globals['_SIMULATIONRUN_CREATE']._serialized_start=5832 + _globals['_SIMULATIONRUN_CREATE']._serialized_end=6213 + _globals['_SIMULATIONRUN_CREATE_REQUEST']._serialized_start=5843 + _globals['_SIMULATIONRUN_CREATE_REQUEST']._serialized_end=6111 + _globals['_SIMULATIONRUN_CREATE_RESPONSE']._serialized_start=6113 + _globals['_SIMULATIONRUN_CREATE_RESPONSE']._serialized_end=6213 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD']._serialized_start=6215 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD']._serialized_end=6331 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD_REQUEST']._serialized_start=6238 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD_REQUEST']._serialized_end=6319 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD_RESPONSE']._serialized_start=6113 + _globals['_SIMULATIONRUN_CONFIRMSOURCEUPLOAD_RESPONSE']._serialized_end=6123 + _globals['_SIMULATIONRUN_GET']._serialized_start=6333 + _globals['_SIMULATIONRUN_GET']._serialized_end=6445 + _globals['_SIMULATIONRUN_GET_REQUEST']._serialized_start=6238 + _globals['_SIMULATIONRUN_GET_REQUEST']._serialized_end=6294 + _globals['_SIMULATIONRUN_GET_RESPONSE']._serialized_start=6398 + _globals['_SIMULATIONRUN_GET_RESPONSE']._serialized_end=6445 + _globals['_SIMULATIONRUN_LIST']._serialized_start=6448 + _globals['_SIMULATIONRUN_LIST']._serialized_end=6716 + _globals['_SIMULATIONRUN_LIST_REQUEST']._serialized_start=6457 + _globals['_SIMULATIONRUN_LIST_REQUEST']._serialized_end=6615 + _globals['_SIMULATIONRUN_LIST_RESPONSE']._serialized_start=6617 + _globals['_SIMULATIONRUN_LIST_RESPONSE']._serialized_end=6716 + _globals['_SIMULATIONRUN_CANCEL']._serialized_start=6718 + _globals['_SIMULATIONRUN_CANCEL']._serialized_end=6796 + _globals['_SIMULATIONRUN_CANCEL_REQUEST']._serialized_start=6238 + _globals['_SIMULATIONRUN_CANCEL_REQUEST']._serialized_end=6294 + _globals['_SIMULATIONRUN_CANCEL_RESPONSE']._serialized_start=6113 + _globals['_SIMULATIONRUN_CANCEL_RESPONSE']._serialized_end=6123 + _globals['_SIMULATIONRUN_USAGE']._serialized_start=1531 + _globals['_SIMULATIONRUN_USAGE']._serialized_end=1591 + _globals['_SIMULATIONRUN_STATUS']._serialized_start=6861 + _globals['_SIMULATIONRUN_STATUS']._serialized_end=7026 + _globals['_SCENARIO']._serialized_start=7044 + _globals['_SCENARIO']._serialized_end=7360 + _globals['_SCENARIO_CREATEFROMSESSION']._serialized_start=7183 + _globals['_SCENARIO_CREATEFROMSESSION']._serialized_end=7315 + _globals['_SCENARIO_CREATEFROMSESSION_REQUEST']._serialized_start=7204 + _globals['_SCENARIO_CREATEFROMSESSION_REQUEST']._serialized_end=7266 + _globals['_SCENARIO_CREATEFROMSESSION_RESPONSE']._serialized_start=7268 + _globals['_SCENARIO_CREATEFROMSESSION_RESPONSE']._serialized_end=7315 + _globals['_SCENARIO_TAGSENTRY']._serialized_start=7317 + _globals['_SCENARIO_TAGSENTRY']._serialized_end=7360 + _globals['_SCENARIOGROUP']._serialized_start=7362 + _globals['_SCENARIOGROUP']._serialized_end=7429 + _globals['_SIMULATIONDISPATCH']._serialized_start=7432 + _globals['_SIMULATIONDISPATCH']._serialized_end=7571 + _globals['_AGENTSIMULATION']._serialized_start=7678 + _globals['_AGENTSIMULATION']._serialized_end=8348 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_simulation.pyi b/livekit-protocol/livekit/protocol/agent_simulation.pyi new file mode 100644 index 00000000..24f98eb2 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_simulation.pyi @@ -0,0 +1,499 @@ +from .agent_pb import agent_session as _agent_session +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from . import cloud_agent as _cloud_agent +from . import models as _models +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SimulationMode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SIMULATION_MODE_UNSPECIFIED: _ClassVar[SimulationMode] + SIMULATION_MODE_TEXT: _ClassVar[SimulationMode] + SIMULATION_MODE_AUDIO: _ClassVar[SimulationMode] +SIMULATION_MODE_UNSPECIFIED: SimulationMode +SIMULATION_MODE_TEXT: SimulationMode +SIMULATION_MODE_AUDIO: SimulationMode + +class SimulationRunSummary(_message.Message): + __slots__ = ("passed", "failed", "going_well", "to_improve", "issues", "chat_history") + class ChatHistoryEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: _agent_session.ChatContext + def __init__(self, key: _Optional[str] = ..., value: _Optional[_Union[_agent_session.ChatContext, _Mapping]] = ...) -> None: ... + class Issue(_message.Message): + __slots__ = ("description", "suggestion", "label") + DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + SUGGESTION_FIELD_NUMBER: _ClassVar[int] + LABEL_FIELD_NUMBER: _ClassVar[int] + description: str + suggestion: str + label: str + def __init__(self, description: _Optional[str] = ..., suggestion: _Optional[str] = ..., label: _Optional[str] = ...) -> None: ... + PASSED_FIELD_NUMBER: _ClassVar[int] + FAILED_FIELD_NUMBER: _ClassVar[int] + GOING_WELL_FIELD_NUMBER: _ClassVar[int] + TO_IMPROVE_FIELD_NUMBER: _ClassVar[int] + ISSUES_FIELD_NUMBER: _ClassVar[int] + CHAT_HISTORY_FIELD_NUMBER: _ClassVar[int] + passed: int + failed: int + going_well: str + to_improve: str + issues: _containers.RepeatedCompositeFieldContainer[SimulationRunSummary.Issue] + chat_history: _containers.MessageMap[str, _agent_session.ChatContext] + def __init__(self, passed: _Optional[int] = ..., failed: _Optional[int] = ..., going_well: _Optional[str] = ..., to_improve: _Optional[str] = ..., issues: _Optional[_Iterable[_Union[SimulationRunSummary.Issue, _Mapping]]] = ..., chat_history: _Optional[_Mapping[str, _agent_session.ChatContext]] = ...) -> None: ... + +class SimulationRun(_message.Message): + __slots__ = ("id", "project_id", "status", "agent_description", "error", "created_at", "jobs", "agent_name", "scenario_group", "ended_at", "job_count", "passed_count", "failed_count", "num_simulations", "usage", "concurrency", "mode", "metrics", "summary_zstd") + class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + STATUS_PENDING_UPLOAD: _ClassVar[SimulationRun.Status] + STATUS_GENERATING: _ClassVar[SimulationRun.Status] + STATUS_RUNNING: _ClassVar[SimulationRun.Status] + STATUS_SUMMARIZING: _ClassVar[SimulationRun.Status] + STATUS_COMPLETED: _ClassVar[SimulationRun.Status] + STATUS_FAILED: _ClassVar[SimulationRun.Status] + STATUS_CANCELLED: _ClassVar[SimulationRun.Status] + STATUS_PENDING_UPLOAD: SimulationRun.Status + STATUS_GENERATING: SimulationRun.Status + STATUS_RUNNING: SimulationRun.Status + STATUS_SUMMARIZING: SimulationRun.Status + STATUS_COMPLETED: SimulationRun.Status + STATUS_FAILED: SimulationRun.Status + STATUS_CANCELLED: SimulationRun.Status + class Job(_message.Message): + __slots__ = ("id", "status", "instructions", "error", "agent_expectations", "label", "tags", "room_name", "started_at", "ended_at", "room_id", "usage", "metrics") + class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + STATUS_PENDING: _ClassVar[SimulationRun.Job.Status] + STATUS_RUNNING: _ClassVar[SimulationRun.Job.Status] + STATUS_COMPLETED: _ClassVar[SimulationRun.Job.Status] + STATUS_FAILED: _ClassVar[SimulationRun.Job.Status] + STATUS_CANCELLED: _ClassVar[SimulationRun.Job.Status] + STATUS_PENDING: SimulationRun.Job.Status + STATUS_RUNNING: SimulationRun.Job.Status + STATUS_COMPLETED: SimulationRun.Job.Status + STATUS_FAILED: SimulationRun.Job.Status + STATUS_CANCELLED: SimulationRun.Job.Status + class Usage(_message.Message): + __slots__ = ("text_turns_count", "audio_turns_count") + TEXT_TURNS_COUNT_FIELD_NUMBER: _ClassVar[int] + AUDIO_TURNS_COUNT_FIELD_NUMBER: _ClassVar[int] + text_turns_count: int + audio_turns_count: int + def __init__(self, text_turns_count: _Optional[int] = ..., audio_turns_count: _Optional[int] = ...) -> None: ... + ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + AGENT_EXPECTATIONS_FIELD_NUMBER: _ClassVar[int] + LABEL_FIELD_NUMBER: _ClassVar[int] + TAGS_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + STARTED_AT_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + USAGE_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + id: str + status: SimulationRun.Job.Status + instructions: str + error: str + agent_expectations: str + label: str + tags: _containers.RepeatedScalarFieldContainer[str] + room_name: str + started_at: _timestamp_pb2.Timestamp + ended_at: _timestamp_pb2.Timestamp + room_id: str + usage: SimulationRun.Job.Usage + metrics: SimulationRun.JobMetrics + def __init__(self, id: _Optional[str] = ..., status: _Optional[_Union[SimulationRun.Job.Status, str]] = ..., instructions: _Optional[str] = ..., error: _Optional[str] = ..., agent_expectations: _Optional[str] = ..., label: _Optional[str] = ..., tags: _Optional[_Iterable[str]] = ..., room_name: _Optional[str] = ..., started_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., ended_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., room_id: _Optional[str] = ..., usage: _Optional[_Union[SimulationRun.Job.Usage, _Mapping]] = ..., metrics: _Optional[_Union[SimulationRun.JobMetrics, _Mapping]] = ...) -> None: ... + class JobMetrics(_message.Message): + __slots__ = ("accuracy_score", "experience_score", "task_completion", "stt", "llm", "tts", "conversation", "turns", "judge_model", "has_remote_session", "t0", "conciseness", "unnecessary_tool_calls", "information_loss", "redundant_statements", "poor_question_quality", "conversation_progression") + class STT(_message.Message): + __slots__ = ("wer", "words", "word_errors", "cer", "chars", "char_errors", "stt_delay_ms", "entity_recognition", "entities_uttered", "entities_recognized", "entity_recall", "entities_acquired_uttered", "entities_acquired_recognized") + WER_FIELD_NUMBER: _ClassVar[int] + WORDS_FIELD_NUMBER: _ClassVar[int] + WORD_ERRORS_FIELD_NUMBER: _ClassVar[int] + CER_FIELD_NUMBER: _ClassVar[int] + CHARS_FIELD_NUMBER: _ClassVar[int] + CHAR_ERRORS_FIELD_NUMBER: _ClassVar[int] + STT_DELAY_MS_FIELD_NUMBER: _ClassVar[int] + ENTITY_RECOGNITION_FIELD_NUMBER: _ClassVar[int] + ENTITIES_UTTERED_FIELD_NUMBER: _ClassVar[int] + ENTITIES_RECOGNIZED_FIELD_NUMBER: _ClassVar[int] + ENTITY_RECALL_FIELD_NUMBER: _ClassVar[int] + ENTITIES_ACQUIRED_UTTERED_FIELD_NUMBER: _ClassVar[int] + ENTITIES_ACQUIRED_RECOGNIZED_FIELD_NUMBER: _ClassVar[int] + wer: float + words: int + word_errors: int + cer: float + chars: int + char_errors: int + stt_delay_ms: int + entity_recognition: float + entities_uttered: int + entities_recognized: int + entity_recall: float + entities_acquired_uttered: int + entities_acquired_recognized: int + def __init__(self, wer: _Optional[float] = ..., words: _Optional[int] = ..., word_errors: _Optional[int] = ..., cer: _Optional[float] = ..., chars: _Optional[int] = ..., char_errors: _Optional[int] = ..., stt_delay_ms: _Optional[int] = ..., entity_recognition: _Optional[float] = ..., entities_uttered: _Optional[int] = ..., entities_recognized: _Optional[int] = ..., entity_recall: _Optional[float] = ..., entities_acquired_uttered: _Optional[int] = ..., entities_acquired_recognized: _Optional[int] = ...) -> None: ... + class LLM(_message.Message): + __slots__ = ("ttft_ms", "ttfs_ms", "tokens_per_second") + TTFT_MS_FIELD_NUMBER: _ClassVar[int] + TTFS_MS_FIELD_NUMBER: _ClassVar[int] + TOKENS_PER_SECOND_FIELD_NUMBER: _ClassVar[int] + ttft_ms: int + ttfs_ms: int + tokens_per_second: float + def __init__(self, ttft_ms: _Optional[int] = ..., ttfs_ms: _Optional[int] = ..., tokens_per_second: _Optional[float] = ...) -> None: ... + class TTS(_message.Message): + __slots__ = ("ttfb_ms", "wer", "words", "word_errors", "cer", "chars", "char_errors", "entity_recognition", "entities_uttered", "entities_recognized", "entity_recall", "entities_acquired_uttered", "entities_acquired_recognized") + TTFB_MS_FIELD_NUMBER: _ClassVar[int] + WER_FIELD_NUMBER: _ClassVar[int] + WORDS_FIELD_NUMBER: _ClassVar[int] + WORD_ERRORS_FIELD_NUMBER: _ClassVar[int] + CER_FIELD_NUMBER: _ClassVar[int] + CHARS_FIELD_NUMBER: _ClassVar[int] + CHAR_ERRORS_FIELD_NUMBER: _ClassVar[int] + ENTITY_RECOGNITION_FIELD_NUMBER: _ClassVar[int] + ENTITIES_UTTERED_FIELD_NUMBER: _ClassVar[int] + ENTITIES_RECOGNIZED_FIELD_NUMBER: _ClassVar[int] + ENTITY_RECALL_FIELD_NUMBER: _ClassVar[int] + ENTITIES_ACQUIRED_UTTERED_FIELD_NUMBER: _ClassVar[int] + ENTITIES_ACQUIRED_RECOGNIZED_FIELD_NUMBER: _ClassVar[int] + ttfb_ms: int + wer: float + words: int + word_errors: int + cer: float + chars: int + char_errors: int + entity_recognition: float + entities_uttered: int + entities_recognized: int + entity_recall: float + entities_acquired_uttered: int + entities_acquired_recognized: int + def __init__(self, ttfb_ms: _Optional[int] = ..., wer: _Optional[float] = ..., words: _Optional[int] = ..., word_errors: _Optional[int] = ..., cer: _Optional[float] = ..., chars: _Optional[int] = ..., char_errors: _Optional[int] = ..., entity_recognition: _Optional[float] = ..., entities_uttered: _Optional[int] = ..., entities_recognized: _Optional[int] = ..., entity_recall: _Optional[float] = ..., entities_acquired_uttered: _Optional[int] = ..., entities_acquired_recognized: _Optional[int] = ...) -> None: ... + class Conversation(_message.Message): + __slots__ = ("turn_taking_score", "heard_e2e_latency_p50_ms", "heard_e2e_latency_p95_ms", "heard_e2e_latency_p99_ms", "time_to_yield_ms", "eot_misprediction_count", "overlap_ratio", "overlap_speech_ms", "total_speech_ms", "awkward_silence_count", "unanswered_turns", "false_interruption_count", "e2e_latency_ms") + TURN_TAKING_SCORE_FIELD_NUMBER: _ClassVar[int] + HEARD_E2E_LATENCY_P50_MS_FIELD_NUMBER: _ClassVar[int] + HEARD_E2E_LATENCY_P95_MS_FIELD_NUMBER: _ClassVar[int] + HEARD_E2E_LATENCY_P99_MS_FIELD_NUMBER: _ClassVar[int] + TIME_TO_YIELD_MS_FIELD_NUMBER: _ClassVar[int] + EOT_MISPREDICTION_COUNT_FIELD_NUMBER: _ClassVar[int] + OVERLAP_RATIO_FIELD_NUMBER: _ClassVar[int] + OVERLAP_SPEECH_MS_FIELD_NUMBER: _ClassVar[int] + TOTAL_SPEECH_MS_FIELD_NUMBER: _ClassVar[int] + AWKWARD_SILENCE_COUNT_FIELD_NUMBER: _ClassVar[int] + UNANSWERED_TURNS_FIELD_NUMBER: _ClassVar[int] + FALSE_INTERRUPTION_COUNT_FIELD_NUMBER: _ClassVar[int] + E2E_LATENCY_MS_FIELD_NUMBER: _ClassVar[int] + turn_taking_score: float + heard_e2e_latency_p50_ms: int + heard_e2e_latency_p95_ms: int + heard_e2e_latency_p99_ms: int + time_to_yield_ms: int + eot_misprediction_count: int + overlap_ratio: float + overlap_speech_ms: int + total_speech_ms: int + awkward_silence_count: int + unanswered_turns: int + false_interruption_count: int + e2e_latency_ms: int + def __init__(self, turn_taking_score: _Optional[float] = ..., heard_e2e_latency_p50_ms: _Optional[int] = ..., heard_e2e_latency_p95_ms: _Optional[int] = ..., heard_e2e_latency_p99_ms: _Optional[int] = ..., time_to_yield_ms: _Optional[int] = ..., eot_misprediction_count: _Optional[int] = ..., overlap_ratio: _Optional[float] = ..., overlap_speech_ms: _Optional[int] = ..., total_speech_ms: _Optional[int] = ..., awkward_silence_count: _Optional[int] = ..., unanswered_turns: _Optional[int] = ..., false_interruption_count: _Optional[int] = ..., e2e_latency_ms: _Optional[int] = ...) -> None: ... + class Turn(_message.Message): + __slots__ = ("index", "role", "start_ms", "end_ms", "stt_delay_ms", "llm_ttft_ms", "llm_ttfs_ms", "llm_tps", "tts_ttfb_ms", "e2e_latency_ms", "heard_e2e_latency_ms", "time_to_yield_ms", "turn_taking_score", "conciseness", "eot_misprediction", "awkward_silence", "unanswered") + INDEX_FIELD_NUMBER: _ClassVar[int] + ROLE_FIELD_NUMBER: _ClassVar[int] + START_MS_FIELD_NUMBER: _ClassVar[int] + END_MS_FIELD_NUMBER: _ClassVar[int] + STT_DELAY_MS_FIELD_NUMBER: _ClassVar[int] + LLM_TTFT_MS_FIELD_NUMBER: _ClassVar[int] + LLM_TTFS_MS_FIELD_NUMBER: _ClassVar[int] + LLM_TPS_FIELD_NUMBER: _ClassVar[int] + TTS_TTFB_MS_FIELD_NUMBER: _ClassVar[int] + E2E_LATENCY_MS_FIELD_NUMBER: _ClassVar[int] + HEARD_E2E_LATENCY_MS_FIELD_NUMBER: _ClassVar[int] + TIME_TO_YIELD_MS_FIELD_NUMBER: _ClassVar[int] + TURN_TAKING_SCORE_FIELD_NUMBER: _ClassVar[int] + CONCISENESS_FIELD_NUMBER: _ClassVar[int] + EOT_MISPREDICTION_FIELD_NUMBER: _ClassVar[int] + AWKWARD_SILENCE_FIELD_NUMBER: _ClassVar[int] + UNANSWERED_FIELD_NUMBER: _ClassVar[int] + index: int + role: _agent_session.ChatRole + start_ms: int + end_ms: int + stt_delay_ms: int + llm_ttft_ms: int + llm_ttfs_ms: int + llm_tps: float + tts_ttfb_ms: int + e2e_latency_ms: int + heard_e2e_latency_ms: int + time_to_yield_ms: int + turn_taking_score: float + conciseness: float + eot_misprediction: bool + awkward_silence: bool + unanswered: bool + def __init__(self, index: _Optional[int] = ..., role: _Optional[_Union[_agent_session.ChatRole, str]] = ..., start_ms: _Optional[int] = ..., end_ms: _Optional[int] = ..., stt_delay_ms: _Optional[int] = ..., llm_ttft_ms: _Optional[int] = ..., llm_ttfs_ms: _Optional[int] = ..., llm_tps: _Optional[float] = ..., tts_ttfb_ms: _Optional[int] = ..., e2e_latency_ms: _Optional[int] = ..., heard_e2e_latency_ms: _Optional[int] = ..., time_to_yield_ms: _Optional[int] = ..., turn_taking_score: _Optional[float] = ..., conciseness: _Optional[float] = ..., eot_misprediction: bool = ..., awkward_silence: bool = ..., unanswered: bool = ...) -> None: ... + ACCURACY_SCORE_FIELD_NUMBER: _ClassVar[int] + EXPERIENCE_SCORE_FIELD_NUMBER: _ClassVar[int] + TASK_COMPLETION_FIELD_NUMBER: _ClassVar[int] + STT_FIELD_NUMBER: _ClassVar[int] + LLM_FIELD_NUMBER: _ClassVar[int] + TTS_FIELD_NUMBER: _ClassVar[int] + CONVERSATION_FIELD_NUMBER: _ClassVar[int] + TURNS_FIELD_NUMBER: _ClassVar[int] + JUDGE_MODEL_FIELD_NUMBER: _ClassVar[int] + HAS_REMOTE_SESSION_FIELD_NUMBER: _ClassVar[int] + T0_FIELD_NUMBER: _ClassVar[int] + CONCISENESS_FIELD_NUMBER: _ClassVar[int] + UNNECESSARY_TOOL_CALLS_FIELD_NUMBER: _ClassVar[int] + INFORMATION_LOSS_FIELD_NUMBER: _ClassVar[int] + REDUNDANT_STATEMENTS_FIELD_NUMBER: _ClassVar[int] + POOR_QUESTION_QUALITY_FIELD_NUMBER: _ClassVar[int] + CONVERSATION_PROGRESSION_FIELD_NUMBER: _ClassVar[int] + accuracy_score: float + experience_score: float + task_completion: float + stt: SimulationRun.JobMetrics.STT + llm: SimulationRun.JobMetrics.LLM + tts: SimulationRun.JobMetrics.TTS + conversation: SimulationRun.JobMetrics.Conversation + turns: _containers.RepeatedCompositeFieldContainer[SimulationRun.JobMetrics.Turn] + judge_model: str + has_remote_session: bool + t0: _timestamp_pb2.Timestamp + conciseness: float + unnecessary_tool_calls: bool + information_loss: bool + redundant_statements: bool + poor_question_quality: bool + conversation_progression: float + def __init__(self, accuracy_score: _Optional[float] = ..., experience_score: _Optional[float] = ..., task_completion: _Optional[float] = ..., stt: _Optional[_Union[SimulationRun.JobMetrics.STT, _Mapping]] = ..., llm: _Optional[_Union[SimulationRun.JobMetrics.LLM, _Mapping]] = ..., tts: _Optional[_Union[SimulationRun.JobMetrics.TTS, _Mapping]] = ..., conversation: _Optional[_Union[SimulationRun.JobMetrics.Conversation, _Mapping]] = ..., turns: _Optional[_Iterable[_Union[SimulationRun.JobMetrics.Turn, _Mapping]]] = ..., judge_model: _Optional[str] = ..., has_remote_session: bool = ..., t0: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., conciseness: _Optional[float] = ..., unnecessary_tool_calls: bool = ..., information_loss: bool = ..., redundant_statements: bool = ..., poor_question_quality: bool = ..., conversation_progression: _Optional[float] = ...) -> None: ... + class RunMetrics(_message.Message): + __slots__ = ("accuracy_score", "experience_score", "scenario_pass_rate", "stt", "llm", "tts", "conversation") + ACCURACY_SCORE_FIELD_NUMBER: _ClassVar[int] + EXPERIENCE_SCORE_FIELD_NUMBER: _ClassVar[int] + SCENARIO_PASS_RATE_FIELD_NUMBER: _ClassVar[int] + STT_FIELD_NUMBER: _ClassVar[int] + LLM_FIELD_NUMBER: _ClassVar[int] + TTS_FIELD_NUMBER: _ClassVar[int] + CONVERSATION_FIELD_NUMBER: _ClassVar[int] + accuracy_score: float + experience_score: float + scenario_pass_rate: float + stt: SimulationRun.JobMetrics.STT + llm: SimulationRun.JobMetrics.LLM + tts: SimulationRun.JobMetrics.TTS + conversation: SimulationRun.JobMetrics.Conversation + def __init__(self, accuracy_score: _Optional[float] = ..., experience_score: _Optional[float] = ..., scenario_pass_rate: _Optional[float] = ..., stt: _Optional[_Union[SimulationRun.JobMetrics.STT, _Mapping]] = ..., llm: _Optional[_Union[SimulationRun.JobMetrics.LLM, _Mapping]] = ..., tts: _Optional[_Union[SimulationRun.JobMetrics.TTS, _Mapping]] = ..., conversation: _Optional[_Union[SimulationRun.JobMetrics.Conversation, _Mapping]] = ...) -> None: ... + class Create(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "agent_name", "num_simulations", "region", "scenario_group", "concurrency", "mode") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + NUM_SIMULATIONS_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + SCENARIO_GROUP_FIELD_NUMBER: _ClassVar[int] + CONCURRENCY_FIELD_NUMBER: _ClassVar[int] + MODE_FIELD_NUMBER: _ClassVar[int] + project_id: str + agent_name: str + num_simulations: int + region: str + scenario_group: ScenarioGroup + concurrency: int + mode: SimulationMode + def __init__(self, project_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., num_simulations: _Optional[int] = ..., region: _Optional[str] = ..., scenario_group: _Optional[_Union[ScenarioGroup, _Mapping]] = ..., concurrency: _Optional[int] = ..., mode: _Optional[_Union[SimulationMode, str]] = ...) -> None: ... + class Response(_message.Message): + __slots__ = ("simulation_run_id", "presigned_post_request") + SIMULATION_RUN_ID_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_POST_REQUEST_FIELD_NUMBER: _ClassVar[int] + simulation_run_id: str + presigned_post_request: _cloud_agent.PresignedPostRequest + def __init__(self, simulation_run_id: _Optional[str] = ..., presigned_post_request: _Optional[_Union[_cloud_agent.PresignedPostRequest, _Mapping]] = ...) -> None: ... + def __init__(self) -> None: ... + class ConfirmSourceUpload(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "simulation_run_id", "code_entrypoint") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + SIMULATION_RUN_ID_FIELD_NUMBER: _ClassVar[int] + CODE_ENTRYPOINT_FIELD_NUMBER: _ClassVar[int] + project_id: str + simulation_run_id: str + code_entrypoint: str + def __init__(self, project_id: _Optional[str] = ..., simulation_run_id: _Optional[str] = ..., code_entrypoint: _Optional[str] = ...) -> None: ... + class Response(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + def __init__(self) -> None: ... + class Get(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "simulation_run_id") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + SIMULATION_RUN_ID_FIELD_NUMBER: _ClassVar[int] + project_id: str + simulation_run_id: str + def __init__(self, project_id: _Optional[str] = ..., simulation_run_id: _Optional[str] = ...) -> None: ... + class Response(_message.Message): + __slots__ = ("run",) + RUN_FIELD_NUMBER: _ClassVar[int] + run: SimulationRun + def __init__(self, run: _Optional[_Union[SimulationRun, _Mapping]] = ...) -> None: ... + def __init__(self) -> None: ... + class List(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "status", "page_token") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + project_id: str + status: SimulationRun.Status + page_token: _models.TokenPagination + def __init__(self, project_id: _Optional[str] = ..., status: _Optional[_Union[SimulationRun.Status, str]] = ..., page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ...) -> None: ... + class Response(_message.Message): + __slots__ = ("runs", "next_page_token") + RUNS_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] + runs: _containers.RepeatedCompositeFieldContainer[SimulationRun] + next_page_token: _models.TokenPagination + def __init__(self, runs: _Optional[_Iterable[_Union[SimulationRun, _Mapping]]] = ..., next_page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ...) -> None: ... + def __init__(self) -> None: ... + class Cancel(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "simulation_run_id") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + SIMULATION_RUN_ID_FIELD_NUMBER: _ClassVar[int] + project_id: str + simulation_run_id: str + def __init__(self, project_id: _Optional[str] = ..., simulation_run_id: _Optional[str] = ...) -> None: ... + class Response(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + def __init__(self) -> None: ... + class Usage(_message.Message): + __slots__ = ("text_turns_count", "audio_turns_count") + TEXT_TURNS_COUNT_FIELD_NUMBER: _ClassVar[int] + AUDIO_TURNS_COUNT_FIELD_NUMBER: _ClassVar[int] + text_turns_count: int + audio_turns_count: int + def __init__(self, text_turns_count: _Optional[int] = ..., audio_turns_count: _Optional[int] = ...) -> None: ... + ID_FIELD_NUMBER: _ClassVar[int] + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + AGENT_DESCRIPTION_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + JOBS_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + SCENARIO_GROUP_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_FIELD_NUMBER: _ClassVar[int] + JOB_COUNT_FIELD_NUMBER: _ClassVar[int] + PASSED_COUNT_FIELD_NUMBER: _ClassVar[int] + FAILED_COUNT_FIELD_NUMBER: _ClassVar[int] + NUM_SIMULATIONS_FIELD_NUMBER: _ClassVar[int] + USAGE_FIELD_NUMBER: _ClassVar[int] + CONCURRENCY_FIELD_NUMBER: _ClassVar[int] + MODE_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + SUMMARY_ZSTD_FIELD_NUMBER: _ClassVar[int] + id: str + project_id: str + status: SimulationRun.Status + agent_description: str + error: str + created_at: _timestamp_pb2.Timestamp + jobs: _containers.RepeatedCompositeFieldContainer[SimulationRun.Job] + agent_name: str + scenario_group: ScenarioGroup + ended_at: _timestamp_pb2.Timestamp + job_count: int + passed_count: int + failed_count: int + num_simulations: int + usage: SimulationRun.Usage + concurrency: int + mode: SimulationMode + metrics: SimulationRun.RunMetrics + summary_zstd: bytes + def __init__(self, id: _Optional[str] = ..., project_id: _Optional[str] = ..., status: _Optional[_Union[SimulationRun.Status, str]] = ..., agent_description: _Optional[str] = ..., error: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., jobs: _Optional[_Iterable[_Union[SimulationRun.Job, _Mapping]]] = ..., agent_name: _Optional[str] = ..., scenario_group: _Optional[_Union[ScenarioGroup, _Mapping]] = ..., ended_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., job_count: _Optional[int] = ..., passed_count: _Optional[int] = ..., failed_count: _Optional[int] = ..., num_simulations: _Optional[int] = ..., usage: _Optional[_Union[SimulationRun.Usage, _Mapping]] = ..., concurrency: _Optional[int] = ..., mode: _Optional[_Union[SimulationMode, str]] = ..., metrics: _Optional[_Union[SimulationRun.RunMetrics, _Mapping]] = ..., summary_zstd: _Optional[bytes] = ...) -> None: ... + +class Scenario(_message.Message): + __slots__ = ("label", "instructions", "agent_expectations", "tags", "userdata") + class CreateFromSession(_message.Message): + __slots__ = () + class Request(_message.Message): + __slots__ = ("project_id", "room_id", "region") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + project_id: str + room_id: str + region: str + def __init__(self, project_id: _Optional[str] = ..., room_id: _Optional[str] = ..., region: _Optional[str] = ...) -> None: ... + class Response(_message.Message): + __slots__ = ("scenario",) + SCENARIO_FIELD_NUMBER: _ClassVar[int] + scenario: Scenario + def __init__(self, scenario: _Optional[_Union[Scenario, _Mapping]] = ...) -> None: ... + def __init__(self) -> None: ... + class TagsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + LABEL_FIELD_NUMBER: _ClassVar[int] + INSTRUCTIONS_FIELD_NUMBER: _ClassVar[int] + AGENT_EXPECTATIONS_FIELD_NUMBER: _ClassVar[int] + TAGS_FIELD_NUMBER: _ClassVar[int] + USERDATA_FIELD_NUMBER: _ClassVar[int] + label: str + instructions: str + agent_expectations: str + tags: _containers.ScalarMap[str, str] + userdata: str + def __init__(self, label: _Optional[str] = ..., instructions: _Optional[str] = ..., agent_expectations: _Optional[str] = ..., tags: _Optional[_Mapping[str, str]] = ..., userdata: _Optional[str] = ...) -> None: ... + +class ScenarioGroup(_message.Message): + __slots__ = ("name", "scenarios") + NAME_FIELD_NUMBER: _ClassVar[int] + SCENARIOS_FIELD_NUMBER: _ClassVar[int] + name: str + scenarios: _containers.RepeatedCompositeFieldContainer[Scenario] + def __init__(self, name: _Optional[str] = ..., scenarios: _Optional[_Iterable[_Union[Scenario, _Mapping]]] = ...) -> None: ... + +class SimulationDispatch(_message.Message): + __slots__ = ("simulation_run_id", "job_id", "scenario", "mode") + SIMULATION_RUN_ID_FIELD_NUMBER: _ClassVar[int] + JOB_ID_FIELD_NUMBER: _ClassVar[int] + SCENARIO_FIELD_NUMBER: _ClassVar[int] + MODE_FIELD_NUMBER: _ClassVar[int] + simulation_run_id: str + job_id: str + scenario: Scenario + mode: SimulationMode + def __init__(self, simulation_run_id: _Optional[str] = ..., job_id: _Optional[str] = ..., scenario: _Optional[_Union[Scenario, _Mapping]] = ..., mode: _Optional[_Union[SimulationMode, str]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/agent_worker.py b/livekit-protocol/livekit/protocol/agent_worker.py new file mode 100644 index 00000000..92af7120 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_worker.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_agent_worker.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_agent_worker.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1alivekit_agent_worker.proto\x12\x07livekit\"\x8e\x01\n\nWorkerInfo\x12\x13\n\x0bworker_type\x18\x01 \x01(\t\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x13\n\x0b\x61\x63tive_jobs\x18\x03 \x01(\x02\x12\x13\n\x0bsdk_version\x18\x04 \x01(\t\x12\x13\n\x0bworker_load\x18\x05 \x01(\x02\x12\x18\n\x10protocol_version\x18\x06 \x01(\x05\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_worker', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_WORKERINFO']._serialized_start=40 + _globals['_WORKERINFO']._serialized_end=182 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/agent_worker.pyi b/livekit-protocol/livekit/protocol/agent_worker.pyi new file mode 100644 index 00000000..658f9a60 --- /dev/null +++ b/livekit-protocol/livekit/protocol/agent_worker.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Optional as _Optional + +DESCRIPTOR: _descriptor.FileDescriptor + +class WorkerInfo(_message.Message): + __slots__ = ("worker_type", "agent_name", "active_jobs", "sdk_version", "worker_load", "protocol_version") + WORKER_TYPE_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + ACTIVE_JOBS_FIELD_NUMBER: _ClassVar[int] + SDK_VERSION_FIELD_NUMBER: _ClassVar[int] + WORKER_LOAD_FIELD_NUMBER: _ClassVar[int] + PROTOCOL_VERSION_FIELD_NUMBER: _ClassVar[int] + worker_type: str + agent_name: str + active_jobs: float + sdk_version: str + worker_load: float + protocol_version: int + def __init__(self, worker_type: _Optional[str] = ..., agent_name: _Optional[str] = ..., active_jobs: _Optional[float] = ..., sdk_version: _Optional[str] = ..., worker_load: _Optional[float] = ..., protocol_version: _Optional[int] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/analytics.py b/livekit-protocol/livekit/protocol/analytics.py index 15fef1ba..323e28a4 100644 --- a/livekit-protocol/livekit/protocol/analytics.py +++ b/livekit-protocol/livekit/protocol/analytics.py @@ -1,53 +1,144 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_analytics.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_analytics.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 -from . import models as _models_ from . import egress as _egress_ from . import ingress as _ingress_ +from . import models as _models_ +from . import room as _room_ +from . import sip as _sip_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17livekit_analytics.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14livekit_models.proto\x1a\x14livekit_egress.proto\x1a\x15livekit_ingress.proto\"T\n\x13\x41nalyticsVideoLayer\x12\r\n\x05layer\x18\x01 \x01(\x05\x12\x0f\n\x07packets\x18\x02 \x01(\r\x12\r\n\x05\x62ytes\x18\x03 \x01(\x04\x12\x0e\n\x06\x66rames\x18\x04 \x01(\r\"\xb5\x03\n\x0f\x41nalyticsStream\x12\x0c\n\x04ssrc\x18\x01 \x01(\r\x12\x17\n\x0fprimary_packets\x18\x02 \x01(\r\x12\x15\n\rprimary_bytes\x18\x03 \x01(\x04\x12\x1a\n\x12retransmit_packets\x18\x04 \x01(\r\x12\x18\n\x10retransmit_bytes\x18\x05 \x01(\x04\x12\x17\n\x0fpadding_packets\x18\x06 \x01(\r\x12\x15\n\rpadding_bytes\x18\x07 \x01(\x04\x12\x14\n\x0cpackets_lost\x18\x08 \x01(\r\x12\x0e\n\x06\x66rames\x18\t \x01(\r\x12\x0b\n\x03rtt\x18\n \x01(\r\x12\x0e\n\x06jitter\x18\x0b \x01(\r\x12\r\n\x05nacks\x18\x0c \x01(\r\x12\x0c\n\x04plis\x18\r \x01(\r\x12\x0c\n\x04\x66irs\x18\x0e \x01(\r\x12\x32\n\x0cvideo_layers\x18\x0f \x03(\x0b\x32\x1c.livekit.AnalyticsVideoLayer\x12.\n\nstart_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xd2\x02\n\rAnalyticsStat\x12\n\n\x02id\x18\x0e \x01(\t\x12\x15\n\ranalytics_key\x18\x01 \x01(\t\x12!\n\x04kind\x18\x02 \x01(\x0e\x32\x13.livekit.StreamType\x12.\n\ntime_stamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04node\x18\x04 \x01(\t\x12\x0f\n\x07room_id\x18\x05 \x01(\t\x12\x11\n\troom_name\x18\x06 \x01(\t\x12\x16\n\x0eparticipant_id\x18\x07 \x01(\t\x12\x10\n\x08track_id\x18\x08 \x01(\t\x12\r\n\x05score\x18\t \x01(\x02\x12)\n\x07streams\x18\n \x03(\x0b\x32\x18.livekit.AnalyticsStream\x12\x0c\n\x04mime\x18\x0b \x01(\t\x12\x11\n\tmin_score\x18\x0c \x01(\x02\x12\x14\n\x0cmedian_score\x18\r \x01(\x02\"7\n\x0e\x41nalyticsStats\x12%\n\x05stats\x18\x01 \x03(\x0b\x32\x16.livekit.AnalyticsStat\"\x9a\x02\n\x13\x41nalyticsClientMeta\x12\x0e\n\x06region\x18\x01 \x01(\t\x12\x0c\n\x04node\x18\x02 \x01(\t\x12\x13\n\x0b\x63lient_addr\x18\x03 \x01(\t\x12\x1b\n\x13\x63lient_connect_time\x18\x04 \x01(\r\x12\x17\n\x0f\x63onnection_type\x18\x05 \x01(\t\x12\x32\n\x10reconnect_reason\x18\x06 \x01(\x0e\x32\x18.livekit.ReconnectReason\x12\x15\n\x08geo_hash\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x63ountry\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07isp_asn\x18\t \x01(\rH\x02\x88\x01\x01\x42\x0b\n\t_geo_hashB\n\n\x08_countryB\n\n\x08_isp_asn\"\xda\x05\n\x0e\x41nalyticsEvent\x12\n\n\x02id\x18\x19 \x01(\t\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.livekit.AnalyticsEventType\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0f\n\x07room_id\x18\x03 \x01(\t\x12\x1b\n\x04room\x18\x04 \x01(\x0b\x32\r.livekit.Room\x12\x16\n\x0eparticipant_id\x18\x05 \x01(\t\x12-\n\x0bparticipant\x18\x06 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x10\n\x08track_id\x18\x07 \x01(\t\x12!\n\x05track\x18\x08 \x01(\x0b\x32\x12.livekit.TrackInfo\x12\x15\n\ranalytics_key\x18\n \x01(\t\x12(\n\x0b\x63lient_info\x18\x0b \x01(\x0b\x32\x13.livekit.ClientInfo\x12\x31\n\x0b\x63lient_meta\x18\x0c \x01(\x0b\x32\x1c.livekit.AnalyticsClientMeta\x12\x11\n\tegress_id\x18\r \x01(\t\x12\x12\n\ningress_id\x18\x13 \x01(\t\x12;\n\x1cmax_subscribed_video_quality\x18\x0e \x01(\x0e\x32\x15.livekit.VideoQuality\x12+\n\tpublisher\x18\x0f \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x0c\n\x04mime\x18\x10 \x01(\t\x12#\n\x06\x65gress\x18\x11 \x01(\x0b\x32\x13.livekit.EgressInfo\x12%\n\x07ingress\x18\x12 \x01(\x0b\x32\x14.livekit.IngressInfo\x12\r\n\x05\x65rror\x18\x14 \x01(\t\x12$\n\trtp_stats\x18\x15 \x01(\x0b\x32\x11.livekit.RTPStats\x12\x13\n\x0bvideo_layer\x18\x16 \x01(\x05\x12\x0f\n\x07node_id\x18\x18 \x01(\t\":\n\x0f\x41nalyticsEvents\x12\'\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x17.livekit.AnalyticsEvent\"\xa4\x01\n\x18\x41nalyticsRoomParticipant\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12-\n\x05state\x18\x04 \x01(\x0e\x32\x1e.livekit.ParticipantInfo.State\x12-\n\tjoined_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xa6\x01\n\rAnalyticsRoom\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\nproject_id\x18\x05 \x01(\t\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x0cparticipants\x18\x04 \x03(\x0b\x32!.livekit.AnalyticsRoomParticipant\"\x94\x01\n\x12\x41nalyticsNodeRooms\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x17\n\x0fsequence_number\x18\x02 \x01(\x04\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12%\n\x05rooms\x18\x04 \x03(\x0b\x32\x16.livekit.AnalyticsRoom**\n\nStreamType\x12\x0c\n\x08UPSTREAM\x10\x00\x12\x0e\n\nDOWNSTREAM\x10\x01*\x95\x05\n\x12\x41nalyticsEventType\x12\x10\n\x0cROOM_CREATED\x10\x00\x12\x0e\n\nROOM_ENDED\x10\x01\x12\x16\n\x12PARTICIPANT_JOINED\x10\x02\x12\x14\n\x10PARTICIPANT_LEFT\x10\x03\x12\x13\n\x0fTRACK_PUBLISHED\x10\x04\x12\x1b\n\x17TRACK_PUBLISH_REQUESTED\x10\x14\x12\x15\n\x11TRACK_UNPUBLISHED\x10\x05\x12\x14\n\x10TRACK_SUBSCRIBED\x10\x06\x12\x1d\n\x19TRACK_SUBSCRIBE_REQUESTED\x10\x15\x12\x1a\n\x16TRACK_SUBSCRIBE_FAILED\x10\x19\x12\x16\n\x12TRACK_UNSUBSCRIBED\x10\x07\x12\x1a\n\x16TRACK_PUBLISHED_UPDATE\x10\n\x12\x0f\n\x0bTRACK_MUTED\x10\x17\x12\x11\n\rTRACK_UNMUTED\x10\x18\x12\x17\n\x13TRACK_PUBLISH_STATS\x10\x1a\x12\x19\n\x15TRACK_SUBSCRIBE_STATS\x10\x1b\x12\x16\n\x12PARTICIPANT_ACTIVE\x10\x0b\x12\x17\n\x13PARTICIPANT_RESUMED\x10\x16\x12\x12\n\x0e\x45GRESS_STARTED\x10\x0c\x12\x10\n\x0c\x45GRESS_ENDED\x10\r\x12\x12\n\x0e\x45GRESS_UPDATED\x10\x1c\x12&\n\"TRACK_MAX_SUBSCRIBED_VIDEO_QUALITY\x10\x0e\x12\x0f\n\x0bRECONNECTED\x10\x0f\x12\x13\n\x0fINGRESS_CREATED\x10\x12\x12\x13\n\x0fINGRESS_DELETED\x10\x13\x12\x13\n\x0fINGRESS_STARTED\x10\x10\x12\x11\n\rINGRESS_ENDED\x10\x11\x12\x13\n\x0fINGRESS_UPDATED\x10\x1d\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17livekit_analytics.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14livekit_egress.proto\x1a\x15livekit_ingress.proto\x1a\x14livekit_models.proto\x1a\x12livekit_room.proto\x1a\x11livekit_sip.proto\x1a\x14logger/options.proto\"T\n\x13\x41nalyticsVideoLayer\x12\r\n\x05layer\x18\x01 \x01(\x05\x12\x0f\n\x07packets\x18\x02 \x01(\r\x12\r\n\x05\x62ytes\x18\x03 \x01(\x04\x12\x0e\n\x06\x66rames\x18\x04 \x01(\r\"\xd3\x03\n\x0f\x41nalyticsStream\x12\x0c\n\x04ssrc\x18\x01 \x01(\r\x12\x17\n\x0fprimary_packets\x18\x02 \x01(\r\x12\x15\n\rprimary_bytes\x18\x03 \x01(\x04\x12\x1a\n\x12retransmit_packets\x18\x04 \x01(\r\x12\x18\n\x10retransmit_bytes\x18\x05 \x01(\x04\x12\x17\n\x0fpadding_packets\x18\x06 \x01(\r\x12\x15\n\rpadding_bytes\x18\x07 \x01(\x04\x12\x14\n\x0cpackets_lost\x18\x08 \x01(\r\x12\x0e\n\x06\x66rames\x18\t \x01(\r\x12\x0b\n\x03rtt\x18\n \x01(\r\x12\x0e\n\x06jitter\x18\x0b \x01(\r\x12\r\n\x05nacks\x18\x0c \x01(\r\x12\x0c\n\x04plis\x18\r \x01(\r\x12\x0c\n\x04\x66irs\x18\x0e \x01(\r\x12\x32\n\x0cvideo_layers\x18\x0f \x03(\x0b\x32\x1c.livekit.AnalyticsVideoLayer\x12.\n\nstart_time\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1c\n\x14packets_out_of_order\x18\x13 \x01(\r\"\xfb\x02\n\rAnalyticsStat\x12\n\n\x02id\x18\x0e \x01(\t\x12\x15\n\ranalytics_key\x18\x01 \x01(\t\x12!\n\x04kind\x18\x02 \x01(\x0e\x32\x13.livekit.StreamType\x12.\n\ntime_stamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04node\x18\x04 \x01(\t\x12\x1a\n\x07room_id\x18\x05 \x01(\tB\t\xbaP\x06roomID\x12\x11\n\troom_name\x18\x06 \x01(\t\x12(\n\x0eparticipant_id\x18\x07 \x01(\tB\x10\xbaP\rparticipantID\x12\x1c\n\x08track_id\x18\x08 \x01(\tB\n\xbaP\x07trackID\x12\r\n\x05score\x18\t \x01(\x02\x12)\n\x07streams\x18\n \x03(\x0b\x32\x18.livekit.AnalyticsStream\x12\x0c\n\x04mime\x18\x0b \x01(\t\x12\x11\n\tmin_score\x18\x0c \x01(\x02\x12\x14\n\x0cmedian_score\x18\r \x01(\x02\"7\n\x0e\x41nalyticsStats\x12%\n\x05stats\x18\x01 \x03(\x0b\x32\x16.livekit.AnalyticsStat\"\x9a\x02\n\x13\x41nalyticsClientMeta\x12\x0e\n\x06region\x18\x01 \x01(\t\x12\x0c\n\x04node\x18\x02 \x01(\t\x12\x13\n\x0b\x63lient_addr\x18\x03 \x01(\t\x12\x1b\n\x13\x63lient_connect_time\x18\x04 \x01(\r\x12\x17\n\x0f\x63onnection_type\x18\x05 \x01(\t\x12\x32\n\x10reconnect_reason\x18\x06 \x01(\x0e\x32\x18.livekit.ReconnectReason\x12\x15\n\x08geo_hash\x18\x07 \x01(\tH\x00\x88\x01\x01\x12\x14\n\x07\x63ountry\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x14\n\x07isp_asn\x18\t \x01(\rH\x02\x88\x01\x01\x42\x0b\n\t_geo_hashB\n\n\x08_countryB\n\n\x08_isp_asn\"\x9e\n\n\x0e\x41nalyticsEvent\x12\n\n\x02id\x18\x19 \x01(\t\x12)\n\x04type\x18\x01 \x01(\x0e\x32\x1b.livekit.AnalyticsEventType\x12-\n\ttimestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x1a\n\x07room_id\x18\x03 \x01(\tB\t\xbaP\x06roomID\x12\x1b\n\x04room\x18\x04 \x01(\x0b\x32\r.livekit.Room\x12(\n\x0eparticipant_id\x18\x05 \x01(\tB\x10\xbaP\rparticipantID\x12-\n\x0bparticipant\x18\x06 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x1c\n\x08track_id\x18\x07 \x01(\tB\n\xbaP\x07trackID\x12!\n\x05track\x18\x08 \x01(\x0b\x32\x12.livekit.TrackInfo\x12\x15\n\ranalytics_key\x18\n \x01(\t\x12(\n\x0b\x63lient_info\x18\x0b \x01(\x0b\x32\x13.livekit.ClientInfo\x12\x31\n\x0b\x63lient_meta\x18\x0c \x01(\x0b\x32\x1c.livekit.AnalyticsClientMeta\x12\x1e\n\tegress_id\x18\r \x01(\tB\x0b\xbaP\x08\x65gressID\x12 \n\ningress_id\x18\x13 \x01(\tB\x0c\xbaP\tingressID\x12;\n\x1cmax_subscribed_video_quality\x18\x0e \x01(\x0e\x32\x15.livekit.VideoQuality\x12+\n\tpublisher\x18\x0f \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x0c\n\x04mime\x18\x10 \x01(\t\x12#\n\x06\x65gress\x18\x11 \x01(\x0b\x32\x13.livekit.EgressInfo\x12%\n\x07ingress\x18\x12 \x01(\x0b\x32\x14.livekit.IngressInfo\x12\r\n\x05\x65rror\x18\x14 \x01(\t\x12$\n\trtp_stats\x18\x15 \x01(\x0b\x32\x11.livekit.RTPStats\x12\x13\n\x0bvideo_layer\x18\x16 \x01(\x05\x12\x1a\n\x07node_id\x18\x18 \x01(\tB\t\xbaP\x06nodeID\x12!\n\x0bsip_call_id\x18\x1a \x01(\tB\x0c\xbaP\tsipCallID\x12&\n\x08sip_call\x18\x1b \x01(\x0b\x32\x14.livekit.SIPCallInfo\x12#\n\x0csip_trunk_id\x18\x1c \x01(\tB\r\xbaP\nsipTrunkID\x12\x37\n\x11sip_inbound_trunk\x18\x1d \x01(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\x12\x39\n\x12sip_outbound_trunk\x18\x1e \x01(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\x12\x32\n\x14sip_dispatch_rule_id\x18\x1f \x01(\tB\x14\xbaP\x11sipDispatchRuleID\x12\x37\n\x11sip_dispatch_rule\x18 \x01(\x0b\x32\x1c.livekit.SIPDispatchRuleInfo\x12.\n\x0csip_transfer\x18$ \x01(\x0b\x32\x18.livekit.SIPTransferInfo\x12#\n\x06report\x18! \x01(\x0b\x32\x13.livekit.ReportInfo\x12&\n\x08\x61pi_call\x18\" \x01(\x0b\x32\x14.livekit.APICallInfo\x12%\n\x07webhook\x18# \x01(\x0b\x32\x14.livekit.WebhookInfo\":\n\x0f\x41nalyticsEvents\x12\'\n\x06\x65vents\x18\x01 \x03(\x0b\x32\x17.livekit.AnalyticsEvent\"\xa4\x01\n\x18\x41nalyticsRoomParticipant\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12-\n\x05state\x18\x04 \x01(\x0e\x32\x1e.livekit.ParticipantInfo.State\x12-\n\tjoined_at\x18\x05 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xb4\x01\n\rAnalyticsRoom\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12 \n\nproject_id\x18\x05 \x01(\tB\x0c\xbaP\tprojectID\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x37\n\x0cparticipants\x18\x04 \x03(\x0b\x32!.livekit.AnalyticsRoomParticipant\"\x9f\x01\n\x12\x41nalyticsNodeRooms\x12\x1a\n\x07node_id\x18\x01 \x01(\tB\t\xbaP\x06nodeID\x12\x17\n\x0fsequence_number\x18\x02 \x01(\x04\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12%\n\x05rooms\x18\x04 \x03(\x0b\x32\x16.livekit.AnalyticsRoom\"K\n\nReportInfo\x12\x32\n\rfeature_usage\x18\x01 \x01(\x0b\x32\x19.livekit.FeatureUsageInfoH\x00\x42\t\n\x07message\"i\n\tTimeRange\x12.\n\nstarted_at\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nded_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\x99\x04\n\x10\x46\x65\x61tureUsageInfo\x12\x32\n\x07\x66\x65\x61ture\x18\x01 \x01(\x0e\x32!.livekit.FeatureUsageInfo.Feature\x12 \n\nproject_id\x18\x02 \x01(\tB\x0c\xbaP\tprojectID\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1a\n\x07room_id\x18\x04 \x01(\tB\t\xbaP\x06roomID\x12\x1c\n\x14participant_identity\x18\x05 \x01(\t\x12(\n\x0eparticipant_id\x18\x06 \x01(\tB\x10\xbaP\rparticipantID\x12\x1c\n\x08track_id\x18\x07 \x01(\tB\n\xbaP\x07trackID\x12\'\n\x0btime_ranges\x18\x08 \x03(\x0b\x32\x12.livekit.TimeRange\x12@\n\x0c\x66\x65\x61ture_info\x18\t \x03(\x0b\x32*.livekit.FeatureUsageInfo.FeatureInfoEntry\x1a\x32\n\x10\x46\x65\x61tureInfoEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"{\n\x07\x46\x65\x61ture\x12\x1c\n\x18KRISP_NOISE_CANCELLATION\x10\x00\x12\'\n#KRISP_BACKGROUND_VOICE_CANCELLATION\x10\x01\x12\x19\n\x15\x41IC_AUDIO_ENHANCEMENT\x10\x02\x12\x0e\n\nKRISP_VIVA\x10\x03\"\xb3\x05\n\x0e\x41PICallRequest\x12\x39\n\x13\x63reate_room_request\x18\x01 \x01(\x0b\x32\x1a.livekit.CreateRoomRequestH\x00\x12\x37\n\x12list_rooms_request\x18\x02 \x01(\x0b\x32\x19.livekit.ListRoomsRequestH\x00\x12\x39\n\x13\x64\x65lete_room_request\x18\x03 \x01(\x0b\x32\x1a.livekit.DeleteRoomRequestH\x00\x12\x45\n\x19list_participants_request\x18\x04 \x01(\x0b\x32 .livekit.ListParticipantsRequestH\x00\x12\x45\n\x19room_participant_identity\x18\x05 \x01(\x0b\x32 .livekit.RoomParticipantIdentityH\x00\x12@\n\x17mute_room_track_request\x18\x06 \x01(\x0b\x32\x1d.livekit.MuteRoomTrackRequestH\x00\x12G\n\x1aupdate_participant_request\x18\x07 \x01(\x0b\x32!.livekit.UpdateParticipantRequestH\x00\x12K\n\x1cupdate_subscriptions_request\x18\x08 \x01(\x0b\x32#.livekit.UpdateSubscriptionsRequestH\x00\x12\x35\n\x11send_data_request\x18\t \x01(\x0b\x32\x18.livekit.SendDataRequestH\x00\x12J\n\x1cupdate_room_metadata_request\x18\n \x01(\x0b\x32\".livekit.UpdateRoomMetadataRequestH\x00\x42\t\n\x07message\"\xb7\x03\n\x0b\x41PICallInfo\x12 \n\nproject_id\x18\x01 \x01(\tB\x0c\xbaP\tprojectID\x12(\n\x07request\x18\x02 \x01(\x0b\x32\x17.livekit.APICallRequest\x12\x0f\n\x07service\x18\x03 \x01(\t\x12\x0e\n\x06method\x18\x04 \x01(\t\x12\x1a\n\x07node_id\x18\x05 \x01(\tB\t\xbaP\x06nodeID\x12\x0e\n\x06status\x18\x06 \x01(\x05\x12\x18\n\x10twirp_error_code\x18\x07 \x01(\t\x12\x1b\n\x13twirp_error_message\x18\x08 \x01(\t\x12\x11\n\troom_name\x18\t \x01(\t\x12\x1a\n\x07room_id\x18\n \x01(\tB\t\xbaP\x06roomID\x12\x1c\n\x14participant_identity\x18\x0b \x01(\t\x12(\n\x0eparticipant_id\x18\x0c \x01(\tB\x10\xbaP\rparticipantID\x12\x1c\n\x08track_id\x18\r \x01(\tB\n\xbaP\x07trackID\x12.\n\nstarted_at\x18\x0e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x13\n\x0b\x64uration_ns\x18\x0f \x01(\x03\"\xa3\x05\n\x0bWebhookInfo\x12\x1c\n\x08\x65vent_id\x18\x01 \x01(\tB\n\xbaP\x07\x65ventID\x12\r\n\x05\x65vent\x18\x02 \x01(\t\x12 \n\nproject_id\x18\x03 \x01(\tB\x0c\xbaP\tprojectID\x12\x11\n\troom_name\x18\x04 \x01(\t\x12\x1a\n\x07room_id\x18\x05 \x01(\tB\t\xbaP\x06roomID\x12\x1c\n\x14participant_identity\x18\x06 \x01(\t\x12(\n\x0eparticipant_id\x18\x07 \x01(\tB\x10\xbaP\rparticipantID\x12\x1c\n\x08track_id\x18\x08 \x01(\tB\n\xbaP\x07trackID\x12\x1e\n\tegress_id\x18\t \x01(\tB\x0b\xbaP\x08\x65gressID\x12 \n\ningress_id\x18\n \x01(\tB\x0c\xbaP\tingressID\x12.\n\ncreated_at\x18\x0b \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12-\n\tqueued_at\x18\x0c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x19\n\x11queue_duration_ns\x18\r \x01(\x03\x12+\n\x07sent_at\x18\x0e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x18\n\x10send_duration_ns\x18\x0f \x01(\x03\x12\x0b\n\x03url\x18\x10 \x01(\t\x12\x13\n\x0bnum_dropped\x18\x11 \x01(\x05\x12\x12\n\nis_dropped\x18\x12 \x01(\x08\x12\x16\n\x0eservice_status\x18\x13 \x01(\t\x12\x1a\n\x12service_error_code\x18\x14 \x01(\x05\x12\x15\n\rservice_error\x18\x15 \x01(\t\x12\x12\n\nsend_error\x18\x16 \x01(\t\x12\x18\n\x10http_status_code\x18\x17 \x01(\x05**\n\nStreamType\x12\x0c\n\x08UPSTREAM\x10\x00\x12\x0e\n\nDOWNSTREAM\x10\x01*\xc6\x08\n\x12\x41nalyticsEventType\x12\x10\n\x0cROOM_CREATED\x10\x00\x12\x0e\n\nROOM_ENDED\x10\x01\x12\x16\n\x12PARTICIPANT_JOINED\x10\x02\x12\x14\n\x10PARTICIPANT_LEFT\x10\x03\x12\"\n\x1ePARTICIPANT_CONNECTION_ABORTED\x10-\x12\x13\n\x0fTRACK_PUBLISHED\x10\x04\x12\x1b\n\x17TRACK_PUBLISH_REQUESTED\x10\x14\x12\x15\n\x11TRACK_UNPUBLISHED\x10\x05\x12\x14\n\x10TRACK_SUBSCRIBED\x10\x06\x12\x1d\n\x19TRACK_SUBSCRIBE_REQUESTED\x10\x15\x12\x1a\n\x16TRACK_SUBSCRIBE_FAILED\x10\x19\x12\x16\n\x12TRACK_UNSUBSCRIBED\x10\x07\x12\x1a\n\x16TRACK_PUBLISHED_UPDATE\x10\n\x12\x0f\n\x0bTRACK_MUTED\x10\x17\x12\x11\n\rTRACK_UNMUTED\x10\x18\x12\x17\n\x13TRACK_PUBLISH_STATS\x10\x1a\x12\x19\n\x15TRACK_SUBSCRIBE_STATS\x10\x1b\x12\x16\n\x12PARTICIPANT_ACTIVE\x10\x0b\x12\x17\n\x13PARTICIPANT_RESUMED\x10\x16\x12\x12\n\x0e\x45GRESS_STARTED\x10\x0c\x12\x10\n\x0c\x45GRESS_ENDED\x10\r\x12\x12\n\x0e\x45GRESS_UPDATED\x10\x1c\x12&\n\"TRACK_MAX_SUBSCRIBED_VIDEO_QUALITY\x10\x0e\x12\x0f\n\x0bRECONNECTED\x10\x0f\x12\x13\n\x0fINGRESS_CREATED\x10\x12\x12\x13\n\x0fINGRESS_DELETED\x10\x13\x12\x13\n\x0fINGRESS_STARTED\x10\x10\x12\x11\n\rINGRESS_ENDED\x10\x11\x12\x13\n\x0fINGRESS_UPDATED\x10\x1d\x12\x1d\n\x19SIP_INBOUND_TRUNK_CREATED\x10\x1e\x12\x1d\n\x19SIP_INBOUND_TRUNK_DELETED\x10\x1f\x12\x1e\n\x1aSIP_OUTBOUND_TRUNK_CREATED\x10 \x12\x1e\n\x1aSIP_OUTBOUND_TRUNK_DELETED\x10!\x12\x1d\n\x19SIP_DISPATCH_RULE_CREATED\x10\"\x12\x1d\n\x19SIP_DISPATCH_RULE_DELETED\x10#\x12\x1b\n\x17SIP_PARTICIPANT_CREATED\x10$\x12\x15\n\x11SIP_CALL_INCOMING\x10%\x12\x14\n\x10SIP_CALL_STARTED\x10&\x12\x12\n\x0eSIP_CALL_ENDED\x10\'\x12\x1a\n\x16SIP_TRANSFER_REQUESTED\x10+\x12\x19\n\x15SIP_TRANSFER_COMPLETE\x10,\x12\x13\n\x0fSIP_CALL_UPDATE\x10.\x12\n\n\x06REPORT\x10(\x12\x0c\n\x08\x41PI_CALL\x10)\x12\x0b\n\x07WEBHOOK\x10*BFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'analytics', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_STREAMTYPE']._serialized_start=2625 - _globals['_STREAMTYPE']._serialized_end=2667 - _globals['_ANALYTICSEVENTTYPE']._serialized_start=2670 - _globals['_ANALYTICSEVENTTYPE']._serialized_end=3331 - _globals['_ANALYTICSVIDEOLAYER']._serialized_start=136 - _globals['_ANALYTICSVIDEOLAYER']._serialized_end=220 - _globals['_ANALYTICSSTREAM']._serialized_start=223 - _globals['_ANALYTICSSTREAM']._serialized_end=660 - _globals['_ANALYTICSSTAT']._serialized_start=663 - _globals['_ANALYTICSSTAT']._serialized_end=1001 - _globals['_ANALYTICSSTATS']._serialized_start=1003 - _globals['_ANALYTICSSTATS']._serialized_end=1058 - _globals['_ANALYTICSCLIENTMETA']._serialized_start=1061 - _globals['_ANALYTICSCLIENTMETA']._serialized_end=1343 - _globals['_ANALYTICSEVENT']._serialized_start=1346 - _globals['_ANALYTICSEVENT']._serialized_end=2076 - _globals['_ANALYTICSEVENTS']._serialized_start=2078 - _globals['_ANALYTICSEVENTS']._serialized_end=2136 - _globals['_ANALYTICSROOMPARTICIPANT']._serialized_start=2139 - _globals['_ANALYTICSROOMPARTICIPANT']._serialized_end=2303 - _globals['_ANALYTICSROOM']._serialized_start=2306 - _globals['_ANALYTICSROOM']._serialized_end=2472 - _globals['_ANALYTICSNODEROOMS']._serialized_start=2475 - _globals['_ANALYTICSNODEROOMS']._serialized_end=2623 + _globals['_ANALYTICSSTAT'].fields_by_name['room_id']._loaded_options = None + _globals['_ANALYTICSSTAT'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_ANALYTICSSTAT'].fields_by_name['participant_id']._loaded_options = None + _globals['_ANALYTICSSTAT'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_ANALYTICSSTAT'].fields_by_name['track_id']._loaded_options = None + _globals['_ANALYTICSSTAT'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_ANALYTICSEVENT'].fields_by_name['room_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_ANALYTICSEVENT'].fields_by_name['participant_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_ANALYTICSEVENT'].fields_by_name['track_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_ANALYTICSEVENT'].fields_by_name['egress_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_ANALYTICSEVENT'].fields_by_name['ingress_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_ANALYTICSEVENT'].fields_by_name['node_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['node_id']._serialized_options = b'\272P\006nodeID' + _globals['_ANALYTICSEVENT'].fields_by_name['sip_call_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['sip_call_id']._serialized_options = b'\272P\tsipCallID' + _globals['_ANALYTICSEVENT'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_ANALYTICSEVENT'].fields_by_name['sip_dispatch_rule_id']._loaded_options = None + _globals['_ANALYTICSEVENT'].fields_by_name['sip_dispatch_rule_id']._serialized_options = b'\272P\021sipDispatchRuleID' + _globals['_ANALYTICSROOM'].fields_by_name['project_id']._loaded_options = None + _globals['_ANALYTICSROOM'].fields_by_name['project_id']._serialized_options = b'\272P\tprojectID' + _globals['_ANALYTICSNODEROOMS'].fields_by_name['node_id']._loaded_options = None + _globals['_ANALYTICSNODEROOMS'].fields_by_name['node_id']._serialized_options = b'\272P\006nodeID' + _globals['_FEATUREUSAGEINFO_FEATUREINFOENTRY']._loaded_options = None + _globals['_FEATUREUSAGEINFO_FEATUREINFOENTRY']._serialized_options = b'8\001' + _globals['_FEATUREUSAGEINFO'].fields_by_name['project_id']._loaded_options = None + _globals['_FEATUREUSAGEINFO'].fields_by_name['project_id']._serialized_options = b'\272P\tprojectID' + _globals['_FEATUREUSAGEINFO'].fields_by_name['room_id']._loaded_options = None + _globals['_FEATUREUSAGEINFO'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_FEATUREUSAGEINFO'].fields_by_name['participant_id']._loaded_options = None + _globals['_FEATUREUSAGEINFO'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_FEATUREUSAGEINFO'].fields_by_name['track_id']._loaded_options = None + _globals['_FEATUREUSAGEINFO'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_APICALLINFO'].fields_by_name['project_id']._loaded_options = None + _globals['_APICALLINFO'].fields_by_name['project_id']._serialized_options = b'\272P\tprojectID' + _globals['_APICALLINFO'].fields_by_name['node_id']._loaded_options = None + _globals['_APICALLINFO'].fields_by_name['node_id']._serialized_options = b'\272P\006nodeID' + _globals['_APICALLINFO'].fields_by_name['room_id']._loaded_options = None + _globals['_APICALLINFO'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_APICALLINFO'].fields_by_name['participant_id']._loaded_options = None + _globals['_APICALLINFO'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_APICALLINFO'].fields_by_name['track_id']._loaded_options = None + _globals['_APICALLINFO'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_WEBHOOKINFO'].fields_by_name['event_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['event_id']._serialized_options = b'\272P\007eventID' + _globals['_WEBHOOKINFO'].fields_by_name['project_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['project_id']._serialized_options = b'\272P\tprojectID' + _globals['_WEBHOOKINFO'].fields_by_name['room_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_WEBHOOKINFO'].fields_by_name['participant_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_WEBHOOKINFO'].fields_by_name['track_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_WEBHOOKINFO'].fields_by_name['egress_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_WEBHOOKINFO'].fields_by_name['ingress_id']._loaded_options = None + _globals['_WEBHOOKINFO'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_STREAMTYPE']._serialized_start=5900 + _globals['_STREAMTYPE']._serialized_end=5942 + _globals['_ANALYTICSEVENTTYPE']._serialized_start=5945 + _globals['_ANALYTICSEVENTTYPE']._serialized_end=7039 + _globals['_ANALYTICSVIDEOLAYER']._serialized_start=197 + _globals['_ANALYTICSVIDEOLAYER']._serialized_end=281 + _globals['_ANALYTICSSTREAM']._serialized_start=284 + _globals['_ANALYTICSSTREAM']._serialized_end=751 + _globals['_ANALYTICSSTAT']._serialized_start=754 + _globals['_ANALYTICSSTAT']._serialized_end=1133 + _globals['_ANALYTICSSTATS']._serialized_start=1135 + _globals['_ANALYTICSSTATS']._serialized_end=1190 + _globals['_ANALYTICSCLIENTMETA']._serialized_start=1193 + _globals['_ANALYTICSCLIENTMETA']._serialized_end=1475 + _globals['_ANALYTICSEVENT']._serialized_start=1478 + _globals['_ANALYTICSEVENT']._serialized_end=2788 + _globals['_ANALYTICSEVENTS']._serialized_start=2790 + _globals['_ANALYTICSEVENTS']._serialized_end=2848 + _globals['_ANALYTICSROOMPARTICIPANT']._serialized_start=2851 + _globals['_ANALYTICSROOMPARTICIPANT']._serialized_end=3015 + _globals['_ANALYTICSROOM']._serialized_start=3018 + _globals['_ANALYTICSROOM']._serialized_end=3198 + _globals['_ANALYTICSNODEROOMS']._serialized_start=3201 + _globals['_ANALYTICSNODEROOMS']._serialized_end=3360 + _globals['_REPORTINFO']._serialized_start=3362 + _globals['_REPORTINFO']._serialized_end=3437 + _globals['_TIMERANGE']._serialized_start=3439 + _globals['_TIMERANGE']._serialized_end=3544 + _globals['_FEATUREUSAGEINFO']._serialized_start=3547 + _globals['_FEATUREUSAGEINFO']._serialized_end=4084 + _globals['_FEATUREUSAGEINFO_FEATUREINFOENTRY']._serialized_start=3909 + _globals['_FEATUREUSAGEINFO_FEATUREINFOENTRY']._serialized_end=3959 + _globals['_FEATUREUSAGEINFO_FEATURE']._serialized_start=3961 + _globals['_FEATUREUSAGEINFO_FEATURE']._serialized_end=4084 + _globals['_APICALLREQUEST']._serialized_start=4087 + _globals['_APICALLREQUEST']._serialized_end=4778 + _globals['_APICALLINFO']._serialized_start=4781 + _globals['_APICALLINFO']._serialized_end=5220 + _globals['_WEBHOOKINFO']._serialized_start=5223 + _globals['_WEBHOOKINFO']._serialized_end=5898 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/analytics.pyi b/livekit-protocol/livekit/protocol/analytics.pyi index 30cfbd50..3ee817bf 100644 --- a/livekit-protocol/livekit/protocol/analytics.pyi +++ b/livekit-protocol/livekit/protocol/analytics.pyi @@ -1,7 +1,10 @@ from google.protobuf import timestamp_pb2 as _timestamp_pb2 -from . import models as _models from . import egress as _egress from . import ingress as _ingress +from . import models as _models +from . import room as _room +from . import sip as _sip +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -21,6 +24,7 @@ class AnalyticsEventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): ROOM_ENDED: _ClassVar[AnalyticsEventType] PARTICIPANT_JOINED: _ClassVar[AnalyticsEventType] PARTICIPANT_LEFT: _ClassVar[AnalyticsEventType] + PARTICIPANT_CONNECTION_ABORTED: _ClassVar[AnalyticsEventType] TRACK_PUBLISHED: _ClassVar[AnalyticsEventType] TRACK_PUBLISH_REQUESTED: _ClassVar[AnalyticsEventType] TRACK_UNPUBLISHED: _ClassVar[AnalyticsEventType] @@ -45,12 +49,29 @@ class AnalyticsEventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): INGRESS_STARTED: _ClassVar[AnalyticsEventType] INGRESS_ENDED: _ClassVar[AnalyticsEventType] INGRESS_UPDATED: _ClassVar[AnalyticsEventType] + SIP_INBOUND_TRUNK_CREATED: _ClassVar[AnalyticsEventType] + SIP_INBOUND_TRUNK_DELETED: _ClassVar[AnalyticsEventType] + SIP_OUTBOUND_TRUNK_CREATED: _ClassVar[AnalyticsEventType] + SIP_OUTBOUND_TRUNK_DELETED: _ClassVar[AnalyticsEventType] + SIP_DISPATCH_RULE_CREATED: _ClassVar[AnalyticsEventType] + SIP_DISPATCH_RULE_DELETED: _ClassVar[AnalyticsEventType] + SIP_PARTICIPANT_CREATED: _ClassVar[AnalyticsEventType] + SIP_CALL_INCOMING: _ClassVar[AnalyticsEventType] + SIP_CALL_STARTED: _ClassVar[AnalyticsEventType] + SIP_CALL_ENDED: _ClassVar[AnalyticsEventType] + SIP_TRANSFER_REQUESTED: _ClassVar[AnalyticsEventType] + SIP_TRANSFER_COMPLETE: _ClassVar[AnalyticsEventType] + SIP_CALL_UPDATE: _ClassVar[AnalyticsEventType] + REPORT: _ClassVar[AnalyticsEventType] + API_CALL: _ClassVar[AnalyticsEventType] + WEBHOOK: _ClassVar[AnalyticsEventType] UPSTREAM: StreamType DOWNSTREAM: StreamType ROOM_CREATED: AnalyticsEventType ROOM_ENDED: AnalyticsEventType PARTICIPANT_JOINED: AnalyticsEventType PARTICIPANT_LEFT: AnalyticsEventType +PARTICIPANT_CONNECTION_ABORTED: AnalyticsEventType TRACK_PUBLISHED: AnalyticsEventType TRACK_PUBLISH_REQUESTED: AnalyticsEventType TRACK_UNPUBLISHED: AnalyticsEventType @@ -75,6 +96,22 @@ INGRESS_DELETED: AnalyticsEventType INGRESS_STARTED: AnalyticsEventType INGRESS_ENDED: AnalyticsEventType INGRESS_UPDATED: AnalyticsEventType +SIP_INBOUND_TRUNK_CREATED: AnalyticsEventType +SIP_INBOUND_TRUNK_DELETED: AnalyticsEventType +SIP_OUTBOUND_TRUNK_CREATED: AnalyticsEventType +SIP_OUTBOUND_TRUNK_DELETED: AnalyticsEventType +SIP_DISPATCH_RULE_CREATED: AnalyticsEventType +SIP_DISPATCH_RULE_DELETED: AnalyticsEventType +SIP_PARTICIPANT_CREATED: AnalyticsEventType +SIP_CALL_INCOMING: AnalyticsEventType +SIP_CALL_STARTED: AnalyticsEventType +SIP_CALL_ENDED: AnalyticsEventType +SIP_TRANSFER_REQUESTED: AnalyticsEventType +SIP_TRANSFER_COMPLETE: AnalyticsEventType +SIP_CALL_UPDATE: AnalyticsEventType +REPORT: AnalyticsEventType +API_CALL: AnalyticsEventType +WEBHOOK: AnalyticsEventType class AnalyticsVideoLayer(_message.Message): __slots__ = ("layer", "packets", "bytes", "frames") @@ -89,7 +126,7 @@ class AnalyticsVideoLayer(_message.Message): def __init__(self, layer: _Optional[int] = ..., packets: _Optional[int] = ..., bytes: _Optional[int] = ..., frames: _Optional[int] = ...) -> None: ... class AnalyticsStream(_message.Message): - __slots__ = ("ssrc", "primary_packets", "primary_bytes", "retransmit_packets", "retransmit_bytes", "padding_packets", "padding_bytes", "packets_lost", "frames", "rtt", "jitter", "nacks", "plis", "firs", "video_layers", "start_time", "end_time") + __slots__ = ("ssrc", "primary_packets", "primary_bytes", "retransmit_packets", "retransmit_bytes", "padding_packets", "padding_bytes", "packets_lost", "frames", "rtt", "jitter", "nacks", "plis", "firs", "video_layers", "start_time", "end_time", "packets_out_of_order") SSRC_FIELD_NUMBER: _ClassVar[int] PRIMARY_PACKETS_FIELD_NUMBER: _ClassVar[int] PRIMARY_BYTES_FIELD_NUMBER: _ClassVar[int] @@ -107,6 +144,7 @@ class AnalyticsStream(_message.Message): VIDEO_LAYERS_FIELD_NUMBER: _ClassVar[int] START_TIME_FIELD_NUMBER: _ClassVar[int] END_TIME_FIELD_NUMBER: _ClassVar[int] + PACKETS_OUT_OF_ORDER_FIELD_NUMBER: _ClassVar[int] ssrc: int primary_packets: int primary_bytes: int @@ -124,7 +162,8 @@ class AnalyticsStream(_message.Message): video_layers: _containers.RepeatedCompositeFieldContainer[AnalyticsVideoLayer] start_time: _timestamp_pb2.Timestamp end_time: _timestamp_pb2.Timestamp - def __init__(self, ssrc: _Optional[int] = ..., primary_packets: _Optional[int] = ..., primary_bytes: _Optional[int] = ..., retransmit_packets: _Optional[int] = ..., retransmit_bytes: _Optional[int] = ..., padding_packets: _Optional[int] = ..., padding_bytes: _Optional[int] = ..., packets_lost: _Optional[int] = ..., frames: _Optional[int] = ..., rtt: _Optional[int] = ..., jitter: _Optional[int] = ..., nacks: _Optional[int] = ..., plis: _Optional[int] = ..., firs: _Optional[int] = ..., video_layers: _Optional[_Iterable[_Union[AnalyticsVideoLayer, _Mapping]]] = ..., start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., end_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + packets_out_of_order: int + def __init__(self, ssrc: _Optional[int] = ..., primary_packets: _Optional[int] = ..., primary_bytes: _Optional[int] = ..., retransmit_packets: _Optional[int] = ..., retransmit_bytes: _Optional[int] = ..., padding_packets: _Optional[int] = ..., padding_bytes: _Optional[int] = ..., packets_lost: _Optional[int] = ..., frames: _Optional[int] = ..., rtt: _Optional[int] = ..., jitter: _Optional[int] = ..., nacks: _Optional[int] = ..., plis: _Optional[int] = ..., firs: _Optional[int] = ..., video_layers: _Optional[_Iterable[_Union[AnalyticsVideoLayer, _Mapping]]] = ..., start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., end_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., packets_out_of_order: _Optional[int] = ...) -> None: ... class AnalyticsStat(_message.Message): __slots__ = ("id", "analytics_key", "kind", "time_stamp", "node", "room_id", "room_name", "participant_id", "track_id", "score", "streams", "mime", "min_score", "median_score") @@ -187,7 +226,7 @@ class AnalyticsClientMeta(_message.Message): def __init__(self, region: _Optional[str] = ..., node: _Optional[str] = ..., client_addr: _Optional[str] = ..., client_connect_time: _Optional[int] = ..., connection_type: _Optional[str] = ..., reconnect_reason: _Optional[_Union[_models.ReconnectReason, str]] = ..., geo_hash: _Optional[str] = ..., country: _Optional[str] = ..., isp_asn: _Optional[int] = ...) -> None: ... class AnalyticsEvent(_message.Message): - __slots__ = ("id", "type", "timestamp", "room_id", "room", "participant_id", "participant", "track_id", "track", "analytics_key", "client_info", "client_meta", "egress_id", "ingress_id", "max_subscribed_video_quality", "publisher", "mime", "egress", "ingress", "error", "rtp_stats", "video_layer", "node_id") + __slots__ = ("id", "type", "timestamp", "room_id", "room", "participant_id", "participant", "track_id", "track", "analytics_key", "client_info", "client_meta", "egress_id", "ingress_id", "max_subscribed_video_quality", "publisher", "mime", "egress", "ingress", "error", "rtp_stats", "video_layer", "node_id", "sip_call_id", "sip_call", "sip_trunk_id", "sip_inbound_trunk", "sip_outbound_trunk", "sip_dispatch_rule_id", "sip_dispatch_rule", "sip_transfer", "report", "api_call", "webhook") ID_FIELD_NUMBER: _ClassVar[int] TYPE_FIELD_NUMBER: _ClassVar[int] TIMESTAMP_FIELD_NUMBER: _ClassVar[int] @@ -211,6 +250,17 @@ class AnalyticsEvent(_message.Message): RTP_STATS_FIELD_NUMBER: _ClassVar[int] VIDEO_LAYER_FIELD_NUMBER: _ClassVar[int] NODE_ID_FIELD_NUMBER: _ClassVar[int] + SIP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + SIP_CALL_FIELD_NUMBER: _ClassVar[int] + SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + SIP_INBOUND_TRUNK_FIELD_NUMBER: _ClassVar[int] + SIP_OUTBOUND_TRUNK_FIELD_NUMBER: _ClassVar[int] + SIP_DISPATCH_RULE_ID_FIELD_NUMBER: _ClassVar[int] + SIP_DISPATCH_RULE_FIELD_NUMBER: _ClassVar[int] + SIP_TRANSFER_FIELD_NUMBER: _ClassVar[int] + REPORT_FIELD_NUMBER: _ClassVar[int] + API_CALL_FIELD_NUMBER: _ClassVar[int] + WEBHOOK_FIELD_NUMBER: _ClassVar[int] id: str type: AnalyticsEventType timestamp: _timestamp_pb2.Timestamp @@ -234,7 +284,18 @@ class AnalyticsEvent(_message.Message): rtp_stats: _models.RTPStats video_layer: int node_id: str - def __init__(self, id: _Optional[str] = ..., type: _Optional[_Union[AnalyticsEventType, str]] = ..., timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., room_id: _Optional[str] = ..., room: _Optional[_Union[_models.Room, _Mapping]] = ..., participant_id: _Optional[str] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., track_id: _Optional[str] = ..., track: _Optional[_Union[_models.TrackInfo, _Mapping]] = ..., analytics_key: _Optional[str] = ..., client_info: _Optional[_Union[_models.ClientInfo, _Mapping]] = ..., client_meta: _Optional[_Union[AnalyticsClientMeta, _Mapping]] = ..., egress_id: _Optional[str] = ..., ingress_id: _Optional[str] = ..., max_subscribed_video_quality: _Optional[_Union[_models.VideoQuality, str]] = ..., publisher: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., mime: _Optional[str] = ..., egress: _Optional[_Union[_egress.EgressInfo, _Mapping]] = ..., ingress: _Optional[_Union[_ingress.IngressInfo, _Mapping]] = ..., error: _Optional[str] = ..., rtp_stats: _Optional[_Union[_models.RTPStats, _Mapping]] = ..., video_layer: _Optional[int] = ..., node_id: _Optional[str] = ...) -> None: ... + sip_call_id: str + sip_call: _sip.SIPCallInfo + sip_trunk_id: str + sip_inbound_trunk: _sip.SIPInboundTrunkInfo + sip_outbound_trunk: _sip.SIPOutboundTrunkInfo + sip_dispatch_rule_id: str + sip_dispatch_rule: _sip.SIPDispatchRuleInfo + sip_transfer: _sip.SIPTransferInfo + report: ReportInfo + api_call: APICallInfo + webhook: WebhookInfo + def __init__(self, id: _Optional[str] = ..., type: _Optional[_Union[AnalyticsEventType, str]] = ..., timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., room_id: _Optional[str] = ..., room: _Optional[_Union[_models.Room, _Mapping]] = ..., participant_id: _Optional[str] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., track_id: _Optional[str] = ..., track: _Optional[_Union[_models.TrackInfo, _Mapping]] = ..., analytics_key: _Optional[str] = ..., client_info: _Optional[_Union[_models.ClientInfo, _Mapping]] = ..., client_meta: _Optional[_Union[AnalyticsClientMeta, _Mapping]] = ..., egress_id: _Optional[str] = ..., ingress_id: _Optional[str] = ..., max_subscribed_video_quality: _Optional[_Union[_models.VideoQuality, str]] = ..., publisher: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., mime: _Optional[str] = ..., egress: _Optional[_Union[_egress.EgressInfo, _Mapping]] = ..., ingress: _Optional[_Union[_ingress.IngressInfo, _Mapping]] = ..., error: _Optional[str] = ..., rtp_stats: _Optional[_Union[_models.RTPStats, _Mapping]] = ..., video_layer: _Optional[int] = ..., node_id: _Optional[str] = ..., sip_call_id: _Optional[str] = ..., sip_call: _Optional[_Union[_sip.SIPCallInfo, _Mapping]] = ..., sip_trunk_id: _Optional[str] = ..., sip_inbound_trunk: _Optional[_Union[_sip.SIPInboundTrunkInfo, _Mapping]] = ..., sip_outbound_trunk: _Optional[_Union[_sip.SIPOutboundTrunkInfo, _Mapping]] = ..., sip_dispatch_rule_id: _Optional[str] = ..., sip_dispatch_rule: _Optional[_Union[_sip.SIPDispatchRuleInfo, _Mapping]] = ..., sip_transfer: _Optional[_Union[_sip.SIPTransferInfo, _Mapping]] = ..., report: _Optional[_Union[ReportInfo, _Mapping]] = ..., api_call: _Optional[_Union[APICallInfo, _Mapping]] = ..., webhook: _Optional[_Union[WebhookInfo, _Mapping]] = ...) -> None: ... class AnalyticsEvents(_message.Message): __slots__ = ("events",) @@ -281,3 +342,164 @@ class AnalyticsNodeRooms(_message.Message): timestamp: _timestamp_pb2.Timestamp rooms: _containers.RepeatedCompositeFieldContainer[AnalyticsRoom] def __init__(self, node_id: _Optional[str] = ..., sequence_number: _Optional[int] = ..., timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., rooms: _Optional[_Iterable[_Union[AnalyticsRoom, _Mapping]]] = ...) -> None: ... + +class ReportInfo(_message.Message): + __slots__ = ("feature_usage",) + FEATURE_USAGE_FIELD_NUMBER: _ClassVar[int] + feature_usage: FeatureUsageInfo + def __init__(self, feature_usage: _Optional[_Union[FeatureUsageInfo, _Mapping]] = ...) -> None: ... + +class TimeRange(_message.Message): + __slots__ = ("started_at", "ended_at") + STARTED_AT_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_FIELD_NUMBER: _ClassVar[int] + started_at: _timestamp_pb2.Timestamp + ended_at: _timestamp_pb2.Timestamp + def __init__(self, started_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., ended_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class FeatureUsageInfo(_message.Message): + __slots__ = ("feature", "project_id", "room_name", "room_id", "participant_identity", "participant_id", "track_id", "time_ranges", "feature_info") + class Feature(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + KRISP_NOISE_CANCELLATION: _ClassVar[FeatureUsageInfo.Feature] + KRISP_BACKGROUND_VOICE_CANCELLATION: _ClassVar[FeatureUsageInfo.Feature] + AIC_AUDIO_ENHANCEMENT: _ClassVar[FeatureUsageInfo.Feature] + KRISP_VIVA: _ClassVar[FeatureUsageInfo.Feature] + KRISP_NOISE_CANCELLATION: FeatureUsageInfo.Feature + KRISP_BACKGROUND_VOICE_CANCELLATION: FeatureUsageInfo.Feature + AIC_AUDIO_ENHANCEMENT: FeatureUsageInfo.Feature + KRISP_VIVA: FeatureUsageInfo.Feature + class FeatureInfoEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + FEATURE_FIELD_NUMBER: _ClassVar[int] + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ID_FIELD_NUMBER: _ClassVar[int] + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + TIME_RANGES_FIELD_NUMBER: _ClassVar[int] + FEATURE_INFO_FIELD_NUMBER: _ClassVar[int] + feature: FeatureUsageInfo.Feature + project_id: str + room_name: str + room_id: str + participant_identity: str + participant_id: str + track_id: str + time_ranges: _containers.RepeatedCompositeFieldContainer[TimeRange] + feature_info: _containers.ScalarMap[str, str] + def __init__(self, feature: _Optional[_Union[FeatureUsageInfo.Feature, str]] = ..., project_id: _Optional[str] = ..., room_name: _Optional[str] = ..., room_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_id: _Optional[str] = ..., track_id: _Optional[str] = ..., time_ranges: _Optional[_Iterable[_Union[TimeRange, _Mapping]]] = ..., feature_info: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class APICallRequest(_message.Message): + __slots__ = ("create_room_request", "list_rooms_request", "delete_room_request", "list_participants_request", "room_participant_identity", "mute_room_track_request", "update_participant_request", "update_subscriptions_request", "send_data_request", "update_room_metadata_request") + CREATE_ROOM_REQUEST_FIELD_NUMBER: _ClassVar[int] + LIST_ROOMS_REQUEST_FIELD_NUMBER: _ClassVar[int] + DELETE_ROOM_REQUEST_FIELD_NUMBER: _ClassVar[int] + LIST_PARTICIPANTS_REQUEST_FIELD_NUMBER: _ClassVar[int] + ROOM_PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + MUTE_ROOM_TRACK_REQUEST_FIELD_NUMBER: _ClassVar[int] + UPDATE_PARTICIPANT_REQUEST_FIELD_NUMBER: _ClassVar[int] + UPDATE_SUBSCRIPTIONS_REQUEST_FIELD_NUMBER: _ClassVar[int] + SEND_DATA_REQUEST_FIELD_NUMBER: _ClassVar[int] + UPDATE_ROOM_METADATA_REQUEST_FIELD_NUMBER: _ClassVar[int] + create_room_request: _room.CreateRoomRequest + list_rooms_request: _room.ListRoomsRequest + delete_room_request: _room.DeleteRoomRequest + list_participants_request: _room.ListParticipantsRequest + room_participant_identity: _room.RoomParticipantIdentity + mute_room_track_request: _room.MuteRoomTrackRequest + update_participant_request: _room.UpdateParticipantRequest + update_subscriptions_request: _room.UpdateSubscriptionsRequest + send_data_request: _room.SendDataRequest + update_room_metadata_request: _room.UpdateRoomMetadataRequest + def __init__(self, create_room_request: _Optional[_Union[_room.CreateRoomRequest, _Mapping]] = ..., list_rooms_request: _Optional[_Union[_room.ListRoomsRequest, _Mapping]] = ..., delete_room_request: _Optional[_Union[_room.DeleteRoomRequest, _Mapping]] = ..., list_participants_request: _Optional[_Union[_room.ListParticipantsRequest, _Mapping]] = ..., room_participant_identity: _Optional[_Union[_room.RoomParticipantIdentity, _Mapping]] = ..., mute_room_track_request: _Optional[_Union[_room.MuteRoomTrackRequest, _Mapping]] = ..., update_participant_request: _Optional[_Union[_room.UpdateParticipantRequest, _Mapping]] = ..., update_subscriptions_request: _Optional[_Union[_room.UpdateSubscriptionsRequest, _Mapping]] = ..., send_data_request: _Optional[_Union[_room.SendDataRequest, _Mapping]] = ..., update_room_metadata_request: _Optional[_Union[_room.UpdateRoomMetadataRequest, _Mapping]] = ...) -> None: ... + +class APICallInfo(_message.Message): + __slots__ = ("project_id", "request", "service", "method", "node_id", "status", "twirp_error_code", "twirp_error_message", "room_name", "room_id", "participant_identity", "participant_id", "track_id", "started_at", "duration_ns") + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + REQUEST_FIELD_NUMBER: _ClassVar[int] + SERVICE_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + NODE_ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + TWIRP_ERROR_CODE_FIELD_NUMBER: _ClassVar[int] + TWIRP_ERROR_MESSAGE_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ID_FIELD_NUMBER: _ClassVar[int] + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + STARTED_AT_FIELD_NUMBER: _ClassVar[int] + DURATION_NS_FIELD_NUMBER: _ClassVar[int] + project_id: str + request: APICallRequest + service: str + method: str + node_id: str + status: int + twirp_error_code: str + twirp_error_message: str + room_name: str + room_id: str + participant_identity: str + participant_id: str + track_id: str + started_at: _timestamp_pb2.Timestamp + duration_ns: int + def __init__(self, project_id: _Optional[str] = ..., request: _Optional[_Union[APICallRequest, _Mapping]] = ..., service: _Optional[str] = ..., method: _Optional[str] = ..., node_id: _Optional[str] = ..., status: _Optional[int] = ..., twirp_error_code: _Optional[str] = ..., twirp_error_message: _Optional[str] = ..., room_name: _Optional[str] = ..., room_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_id: _Optional[str] = ..., track_id: _Optional[str] = ..., started_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., duration_ns: _Optional[int] = ...) -> None: ... + +class WebhookInfo(_message.Message): + __slots__ = ("event_id", "event", "project_id", "room_name", "room_id", "participant_identity", "participant_id", "track_id", "egress_id", "ingress_id", "created_at", "queued_at", "queue_duration_ns", "sent_at", "send_duration_ns", "url", "num_dropped", "is_dropped", "service_status", "service_error_code", "service_error", "send_error", "http_status_code") + EVENT_ID_FIELD_NUMBER: _ClassVar[int] + EVENT_FIELD_NUMBER: _ClassVar[int] + PROJECT_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ID_FIELD_NUMBER: _ClassVar[int] + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + EGRESS_ID_FIELD_NUMBER: _ClassVar[int] + INGRESS_ID_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + QUEUED_AT_FIELD_NUMBER: _ClassVar[int] + QUEUE_DURATION_NS_FIELD_NUMBER: _ClassVar[int] + SENT_AT_FIELD_NUMBER: _ClassVar[int] + SEND_DURATION_NS_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + NUM_DROPPED_FIELD_NUMBER: _ClassVar[int] + IS_DROPPED_FIELD_NUMBER: _ClassVar[int] + SERVICE_STATUS_FIELD_NUMBER: _ClassVar[int] + SERVICE_ERROR_CODE_FIELD_NUMBER: _ClassVar[int] + SERVICE_ERROR_FIELD_NUMBER: _ClassVar[int] + SEND_ERROR_FIELD_NUMBER: _ClassVar[int] + HTTP_STATUS_CODE_FIELD_NUMBER: _ClassVar[int] + event_id: str + event: str + project_id: str + room_name: str + room_id: str + participant_identity: str + participant_id: str + track_id: str + egress_id: str + ingress_id: str + created_at: _timestamp_pb2.Timestamp + queued_at: _timestamp_pb2.Timestamp + queue_duration_ns: int + sent_at: _timestamp_pb2.Timestamp + send_duration_ns: int + url: str + num_dropped: int + is_dropped: bool + service_status: str + service_error_code: int + service_error: str + send_error: str + http_status_code: int + def __init__(self, event_id: _Optional[str] = ..., event: _Optional[str] = ..., project_id: _Optional[str] = ..., room_name: _Optional[str] = ..., room_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_id: _Optional[str] = ..., track_id: _Optional[str] = ..., egress_id: _Optional[str] = ..., ingress_id: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., queued_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., queue_duration_ns: _Optional[int] = ..., sent_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., send_duration_ns: _Optional[int] = ..., url: _Optional[str] = ..., num_dropped: _Optional[int] = ..., is_dropped: bool = ..., service_status: _Optional[str] = ..., service_error_code: _Optional[int] = ..., service_error: _Optional[str] = ..., send_error: _Optional[str] = ..., http_status_code: _Optional[int] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/cloud_agent.py b/livekit-protocol/livekit/protocol/cloud_agent.py new file mode 100644 index 00000000..445d83e8 --- /dev/null +++ b/livekit-protocol/livekit/protocol/cloud_agent.py @@ -0,0 +1,231 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_cloud_agent.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_cloud_agent.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19livekit_cloud_agent.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14logger/options.proto\"q\n\nAgentEvent\x12%\n\x04type\x18\x01 \x01(\x0e\x32\x17.livekit.AgentEventType\x12\r\n\x05\x63ount\x18\x02 \x01(\x05\x12-\n\ttimestamp\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xc7\x01\n\x0b\x41gentSecret\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12&\n\x04kind\x18\x05 \x01(\x0e\x32\x18.livekit.AgentSecretKind\x12\x13\n\x0b\x64\x65ployments\x18\x06 \x03(\t\"\xa9\x01\n\x12\x43reateAgentRequest\x12\x16\n\nagent_name\x18\x01 \x01(\tB\x02\x18\x01\x12%\n\x07secrets\x18\x02 \x03(\x0b\x32\x14.livekit.AgentSecret\x12\x14\n\x08replicas\x18\x03 \x01(\x05\x42\x02\x18\x01\x12\x18\n\x0cmax_replicas\x18\x04 \x01(\x05\x42\x02\x18\x01\x12\x13\n\x07\x63pu_req\x18\x05 \x01(\tB\x02\x18\x01\x12\x0f\n\x07regions\x18\x06 \x03(\t\"\xe3\x01\n\x13\x43reateAgentResponse\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x15\n\rpresigned_url\x18\x05 \x01(\t\x12\x0b\n\x03tag\x18\x06 \x01(\t\x12\x16\n\x0eserver_regions\x18\x07 \x03(\t\x12=\n\x16presigned_post_request\x18\x08 \x01(\x0b\x32\x1d.livekit.PresignedPostRequest\"N\n\x14\x43reateAgentV2Request\x12%\n\x07secrets\x18\x01 \x03(\x0b\x32\x14.livekit.AgentSecret\x12\x0f\n\x07regions\x18\x02 \x03(\t\"]\n\x15\x43reateAgentV2Response\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x0e\n\x06status\x18\x02 \x01(\t\x12\x16\n\x0eserver_regions\x18\x03 \x03(\t\"c\n\x13PromoteAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x16\n\x0esrc_deployment\x18\x02 \x01(\t\x12\x16\n\x0e\x64st_deployment\x18\x03 \x01(\t\"\x9b\x01\n\x14PromoteAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x1c\n\x08\x61gent_id\x18\x03 \x01(\tB\n\xbaP\x07\x61gentID\x12\x16\n\x0esrc_deployment\x18\x04 \x01(\t\x12\x16\n\x0e\x64st_deployment\x18\x05 \x01(\t\x12\x13\n\x0bversion_tag\x18\x06 \x01(\t\"\x8d\x01\n\x14PresignedPostRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x39\n\x06values\x18\x02 \x03(\x0b\x32).livekit.PresignedPostRequest.ValuesEntry\x1a-\n\x0bValuesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbd\x03\n\x0f\x41gentDeployment\x12\x0e\n\x06region\x18\x01 \x01(\t\x12\x1c\n\x08\x61gent_id\x18\x02 \x01(\tB\n\xbaP\x07\x61gentID\x12\x0e\n\x06status\x18\x03 \x01(\t\x12\x10\n\x08replicas\x18\x04 \x01(\x05\x12\x14\n\x0cmin_replicas\x18\x05 \x01(\x05\x12\x14\n\x0cmax_replicas\x18\x06 \x01(\x05\x12\x0f\n\x07\x63pu_req\x18\x07 \x01(\t\x12\x0f\n\x07\x63ur_cpu\x18\x08 \x01(\t\x12\x0f\n\x07\x63ur_mem\x18\t \x01(\t\x12\x0f\n\x07mem_req\x18\n \x01(\t\x12\x11\n\tmem_limit\x18\x0b \x01(\t\x12\x11\n\tcpu_limit\x18\x0c \x01(\t\x12\x15\n\rserver_region\x18\r \x01(\t\x12#\n\x06\x65vents\x18\x0e \x03(\x0b\x32\x13.livekit.AgentEvent\x12\x12\n\ndeployment\x18\x0f \x01(\t\x12\x0f\n\x07version\x18\x10 \x01(\t\x12\x12\n\nagent_name\x18\x11 \x01(\t\x12\x1a\n\x12\x64\x65ployment_enabled\x18\x12 \x01(\x08\x12\x33\n\x0flast_scraped_at\x18\x13 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"\xdb\x01\n\tAgentInfo\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\x12\x33\n\x11\x61gent_deployments\x18\x04 \x03(\x0b\x32\x18.livekit.AgentDeployment\x12%\n\x07secrets\x18\x05 \x03(\x0b\x32\x14.livekit.AgentSecret\x12/\n\x0b\x64\x65ployed_at\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\"E\n\x11ListAgentsRequest\x12\x12\n\nagent_name\x18\x01 \x01(\t\x12\x1c\n\x08\x61gent_id\x18\x02 \x01(\tB\n\xbaP\x07\x61gentID\"8\n\x12ListAgentsResponse\x12\"\n\x06\x61gents\x18\x01 \x03(\x0b\x32\x12.livekit.AgentInfo\"\xc0\x02\n\x0c\x41gentVersion\x12\x0f\n\x07version\x18\x01 \x01(\t\x12\x0f\n\x07\x63urrent\x18\x02 \x01(\x08\x12.\n\ncreated_at\x18\x03 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12/\n\x0b\x64\x65ployed_at\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x39\n\nattributes\x18\x05 \x03(\x0b\x32%.livekit.AgentVersion.AttributesEntry\x12\x0e\n\x06status\x18\x06 \x01(\t\x12\r\n\x05owner\x18\x07 \x01(\t\x12\x10\n\x08\x64raining\x18\x08 \x01(\x08\x12\x0e\n\x06\x61\x63tive\x18\t \x01(\x08\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"L\n\x18ListAgentVersionsRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\"D\n\x19ListAgentVersionsResponse\x12\'\n\x08versions\x18\x01 \x03(\x0b\x32\x15.livekit.AgentVersion\"\xc7\x01\n\x12UpdateAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x16\n\nagent_name\x18\x02 \x01(\tB\x02\x18\x01\x12\x14\n\x08replicas\x18\x03 \x01(\x05\x42\x02\x18\x01\x12\x18\n\x0cmax_replicas\x18\x04 \x01(\x05\x42\x02\x18\x01\x12\x13\n\x07\x63pu_req\x18\x05 \x01(\tB\x02\x18\x01\x12\x0f\n\x07regions\x18\x06 \x03(\t\x12%\n\x07secrets\x18\x07 \x03(\x0b\x32\x14.livekit.AgentSecret\"7\n\x13UpdateAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"3\n\x13RestartAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\"8\n\x14RestartAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"\xbe\x02\n\x12\x44\x65ployAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x16\n\nagent_name\x18\x02 \x01(\tB\x02\x18\x01\x12%\n\x07secrets\x18\x03 \x03(\x0b\x32\x14.livekit.AgentSecret\x12\x14\n\x08replicas\x18\x04 \x01(\x05\x42\x02\x18\x01\x12\x18\n\x0cmax_replicas\x18\x05 \x01(\x05\x42\x02\x18\x01\x12\x13\n\x07\x63pu_req\x18\x06 \x01(\tB\x02\x18\x01\x12\x12\n\ndeployment\x18\x07 \x01(\t\x12?\n\nattributes\x18\x08 \x03(\x0b\x32+.livekit.DeployAgentRequest.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xd0\x01\n\x13\x44\x65ployAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x1c\n\x08\x61gent_id\x18\x03 \x01(\tB\n\xbaP\x07\x61gentID\x12\x19\n\rpresigned_url\x18\x04 \x01(\tB\x02\x18\x01\x12\x0b\n\x03tag\x18\x05 \x01(\t\x12=\n\x16presigned_post_request\x18\x06 \x01(\x0b\x32\x1d.livekit.PresignedPostRequest\x12\x12\n\ndeployment\x18\x07 \x01(\t\"\xe5\x01\n\x14\x44\x65ployAgentV2Request\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12%\n\x07secrets\x18\x02 \x03(\x0b\x32\x14.livekit.AgentSecret\x12\x12\n\ndeployment\x18\x03 \x01(\t\x12\x41\n\nattributes\x18\x04 \x03(\x0b\x32-.livekit.DeployAgentV2Request.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xae\x01\n\x15\x44\x65ployAgentV2Response\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x1c\n\x08\x61gent_id\x18\x03 \x01(\tB\n\xbaP\x07\x61gentID\x12\x0b\n\x03tag\x18\x04 \x01(\t\x12\x34\n\rpresigned_req\x18\x05 \x01(\x0b\x32\x1d.livekit.PresignedPostRequest\x12\x12\n\ndeployment\x18\x06 \x01(\t\"\x97\x01\n\x19UpdateAgentSecretsRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x11\n\toverwrite\x18\x03 \x01(\x08\x12%\n\x07secrets\x18\x04 \x03(\x0b\x32\x14.livekit.AgentSecret\x12\x0e\n\x06remove\x18\x05 \x03(\t\">\n\x1aUpdateAgentSecretsResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"Y\n\x14RollbackAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x0f\n\x07version\x18\x03 \x01(\t\"9\n\x15RollbackAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"Z\n\x12\x44\x65leteAgentRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\x12\x12\n\ndeployment\x18\x03 \x01(\t\"7\n\x13\x44\x65leteAgentResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x0f\n\x07message\x18\x02 \x01(\t\"K\n\x17ListAgentSecretsRequest\x12\x1c\n\x08\x61gent_id\x18\x01 \x01(\tB\n\xbaP\x07\x61gentID\x12\x12\n\nagent_name\x18\x02 \x01(\t\"A\n\x18ListAgentSecretsResponse\x12%\n\x07secrets\x18\x01 \x03(\x0b\x32\x14.livekit.AgentSecret\",\n\rSettingsParam\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\"@\n\x16\x43lientSettingsResponse\x12&\n\x06params\x18\x01 \x03(\x0b\x32\x16.livekit.SettingsParam\"\x17\n\x15\x43lientSettingsRequest\"\x89\x02\n\x0bPrivateLink\x12)\n\x0fprivate_link_id\x18\x01 \x01(\tB\x10\xbaP\rprivateLinkID\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06region\x18\x04 \x01(\t\x12\x0c\n\x04port\x18\x05 \x01(\r\x12\x10\n\x08\x65ndpoint\x18\x06 \x01(\t\x12\x1b\n\x13\x63onnection_endpoint\x18\x07 \x01(\t\x12\x14\n\x0c\x63loud_region\x18\x08 \x01(\t\x12\x31\n\x03\x61ws\x18\x03 \x01(\x0b\x32\x1e.livekit.PrivateLink.AWSConfigB\x02\x18\x01H\x00\x1a!\n\tAWSConfig\x12\x14\n\x08\x65ndpoint\x18\x01 \x01(\tB\x02\x18\x01\x42\x08\n\x06\x63onfig\"\xe8\x02\n\x11PrivateLinkStatus\x12\x31\n\x06status\x18\x01 \x01(\x0e\x32!.livekit.PrivateLinkStatus.Status\x12.\n\nupdated_at\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\xdf\x01\n\x06Status\x12\x1f\n\x1bPRIVATE_LINK_STATUS_UNKNOWN\x10\x00\x12$\n PRIVATE_LINK_STATUS_PROVISIONING\x10\x01\x12(\n$PRIVATE_LINK_STATUS_PENDING_APPROVAL\x10\x02\x12\x1f\n\x1bPRIVATE_LINK_STATUS_HEALTHY\x10\x03\x12!\n\x1dPRIVATE_LINK_STATUS_UNHEALTHY\x10\x04\x12 \n\x1cPRIVATE_LINK_STATUS_APPROVED\x10\x05\"\xfd\x01\n\x18\x43reatePrivateLinkRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06region\x18\x03 \x01(\t\x12\x0c\n\x04port\x18\x04 \x01(\r\x12\x10\n\x08\x65ndpoint\x18\x05 \x01(\t\x12\x19\n\x0c\x63loud_region\x18\x06 \x01(\tH\x01\x88\x01\x01\x12\x44\n\x03\x61ws\x18\x02 \x01(\x0b\x32\x31.livekit.CreatePrivateLinkRequest.AWSCreateConfigB\x02\x18\x01H\x00\x1a\'\n\x0f\x41WSCreateConfig\x12\x14\n\x08\x65ndpoint\x18\x01 \x01(\tB\x02\x18\x01\x42\x08\n\x06\x63onfigB\x0f\n\r_cloud_region\"G\n\x19\x43reatePrivateLinkResponse\x12*\n\x0cprivate_link\x18\x01 \x01(\x0b\x32\x14.livekit.PrivateLink\"F\n\x19\x44\x65stroyPrivateLinkRequest\x12)\n\x0fprivate_link_id\x18\x01 \x01(\tB\x10\xbaP\rprivateLinkID\"\x1c\n\x1a\x44\x65stroyPrivateLinkResponse\"\x19\n\x17ListPrivateLinksRequest\"?\n\x18ListPrivateLinksResponse\x12#\n\x05items\x18\x01 \x03(\x0b\x32\x14.livekit.PrivateLink\"H\n\x1bGetPrivateLinkStatusRequest\x12)\n\x0fprivate_link_id\x18\x01 \x01(\tB\x10\xbaP\rprivateLinkID\"I\n\x1cGetPrivateLinkStatusResponse\x12)\n\x05value\x18\x01 \x01(\x0b\x32\x1a.livekit.PrivateLinkStatus*o\n\x0f\x41gentSecretKind\x12\x1d\n\x19\x41GENT_SECRET_KIND_UNKNOWN\x10\x00\x12!\n\x1d\x41GENT_SECRET_KIND_ENVIRONMENT\x10\x01\x12\x1a\n\x16\x41GENT_SECRET_KIND_FILE\x10\x02*\xba\x01\n\x0e\x41gentEventType\x12\x1c\n\x18\x41GENT_EVENT_TYPE_UNKNOWN\x10\x00\x12(\n$AGENT_EVENT_TYPE_APPLICATION_CRASHED\x10\x01\x12.\n*AGENT_EVENT_TYPE_RESTARTED_HIGH_DISK_USAGE\x10\x02\x12\x30\n,AGENT_EVENT_TYPE_RESTARTED_HIGH_MEMORY_USAGE\x10\x03\x32\x8c\x0c\n\nCloudAgent\x12J\n\x0b\x43reateAgent\x12\x1b.livekit.CreateAgentRequest\x1a\x1c.livekit.CreateAgentResponse\"\x00\x12P\n\rCreateAgentV2\x12\x1d.livekit.CreateAgentV2Request\x1a\x1e.livekit.CreateAgentV2Response\"\x00\x12M\n\x0cPromoteAgent\x12\x1c.livekit.PromoteAgentRequest\x1a\x1d.livekit.PromoteAgentResponse\"\x00\x12G\n\nListAgents\x12\x1a.livekit.ListAgentsRequest\x1a\x1b.livekit.ListAgentsResponse\"\x00\x12\\\n\x11ListAgentVersions\x12!.livekit.ListAgentVersionsRequest\x1a\".livekit.ListAgentVersionsResponse\"\x00\x12Y\n\x10ListAgentSecrets\x12 .livekit.ListAgentSecretsRequest\x1a!.livekit.ListAgentSecretsResponse\"\x00\x12J\n\x0bUpdateAgent\x12\x1b.livekit.UpdateAgentRequest\x1a\x1c.livekit.UpdateAgentResponse\"\x00\x12M\n\x0cRestartAgent\x12\x1c.livekit.RestartAgentRequest\x1a\x1d.livekit.RestartAgentResponse\"\x00\x12J\n\x0b\x44\x65ployAgent\x12\x1b.livekit.DeployAgentRequest\x1a\x1c.livekit.DeployAgentResponse\"\x00\x12P\n\rDeployAgentV2\x12\x1d.livekit.DeployAgentV2Request\x1a\x1e.livekit.DeployAgentV2Response\"\x00\x12_\n\x12UpdateAgentSecrets\x12\".livekit.UpdateAgentSecretsRequest\x1a#.livekit.UpdateAgentSecretsResponse\"\x00\x12P\n\rRollbackAgent\x12\x1d.livekit.RollbackAgentRequest\x1a\x1e.livekit.RollbackAgentResponse\"\x00\x12J\n\x0b\x44\x65leteAgent\x12\x1b.livekit.DeleteAgentRequest\x1a\x1c.livekit.DeleteAgentResponse\"\x00\x12V\n\x11GetClientSettings\x12\x1e.livekit.ClientSettingsRequest\x1a\x1f.livekit.ClientSettingsResponse\"\x00\x12\\\n\x11\x43reatePrivateLink\x12!.livekit.CreatePrivateLinkRequest\x1a\".livekit.CreatePrivateLinkResponse\"\x00\x12_\n\x12\x44\x65stroyPrivateLink\x12\".livekit.DestroyPrivateLinkRequest\x1a#.livekit.DestroyPrivateLinkResponse\"\x00\x12Y\n\x10ListPrivateLinks\x12 .livekit.ListPrivateLinksRequest\x1a!.livekit.ListPrivateLinksResponse\"\x00\x12\x65\n\x14GetPrivateLinkStatus\x12$.livekit.GetPrivateLinkStatusRequest\x1a%.livekit.GetPrivateLinkStatusResponse\"\x00\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'cloud_agent', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_CREATEAGENTREQUEST'].fields_by_name['agent_name']._loaded_options = None + _globals['_CREATEAGENTREQUEST'].fields_by_name['agent_name']._serialized_options = b'\030\001' + _globals['_CREATEAGENTREQUEST'].fields_by_name['replicas']._loaded_options = None + _globals['_CREATEAGENTREQUEST'].fields_by_name['replicas']._serialized_options = b'\030\001' + _globals['_CREATEAGENTREQUEST'].fields_by_name['max_replicas']._loaded_options = None + _globals['_CREATEAGENTREQUEST'].fields_by_name['max_replicas']._serialized_options = b'\030\001' + _globals['_CREATEAGENTREQUEST'].fields_by_name['cpu_req']._loaded_options = None + _globals['_CREATEAGENTREQUEST'].fields_by_name['cpu_req']._serialized_options = b'\030\001' + _globals['_CREATEAGENTRESPONSE'].fields_by_name['agent_id']._loaded_options = None + _globals['_CREATEAGENTRESPONSE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_CREATEAGENTV2RESPONSE'].fields_by_name['agent_id']._loaded_options = None + _globals['_CREATEAGENTV2RESPONSE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_PROMOTEAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_PROMOTEAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_PROMOTEAGENTRESPONSE'].fields_by_name['agent_id']._loaded_options = None + _globals['_PROMOTEAGENTRESPONSE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_PRESIGNEDPOSTREQUEST_VALUESENTRY']._loaded_options = None + _globals['_PRESIGNEDPOSTREQUEST_VALUESENTRY']._serialized_options = b'8\001' + _globals['_AGENTDEPLOYMENT'].fields_by_name['agent_id']._loaded_options = None + _globals['_AGENTDEPLOYMENT'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_AGENTINFO'].fields_by_name['agent_id']._loaded_options = None + _globals['_AGENTINFO'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_LISTAGENTSREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_LISTAGENTSREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_AGENTVERSION_ATTRIBUTESENTRY']._loaded_options = None + _globals['_AGENTVERSION_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_LISTAGENTVERSIONSREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_LISTAGENTVERSIONSREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_UPDATEAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_UPDATEAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_UPDATEAGENTREQUEST'].fields_by_name['agent_name']._loaded_options = None + _globals['_UPDATEAGENTREQUEST'].fields_by_name['agent_name']._serialized_options = b'\030\001' + _globals['_UPDATEAGENTREQUEST'].fields_by_name['replicas']._loaded_options = None + _globals['_UPDATEAGENTREQUEST'].fields_by_name['replicas']._serialized_options = b'\030\001' + _globals['_UPDATEAGENTREQUEST'].fields_by_name['max_replicas']._loaded_options = None + _globals['_UPDATEAGENTREQUEST'].fields_by_name['max_replicas']._serialized_options = b'\030\001' + _globals['_UPDATEAGENTREQUEST'].fields_by_name['cpu_req']._loaded_options = None + _globals['_UPDATEAGENTREQUEST'].fields_by_name['cpu_req']._serialized_options = b'\030\001' + _globals['_RESTARTAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_RESTARTAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_DEPLOYAGENTREQUEST_ATTRIBUTESENTRY']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['agent_name']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['agent_name']._serialized_options = b'\030\001' + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['replicas']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['replicas']._serialized_options = b'\030\001' + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['max_replicas']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['max_replicas']._serialized_options = b'\030\001' + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['cpu_req']._loaded_options = None + _globals['_DEPLOYAGENTREQUEST'].fields_by_name['cpu_req']._serialized_options = b'\030\001' + _globals['_DEPLOYAGENTRESPONSE'].fields_by_name['agent_id']._loaded_options = None + _globals['_DEPLOYAGENTRESPONSE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_DEPLOYAGENTRESPONSE'].fields_by_name['presigned_url']._loaded_options = None + _globals['_DEPLOYAGENTRESPONSE'].fields_by_name['presigned_url']._serialized_options = b'\030\001' + _globals['_DEPLOYAGENTV2REQUEST_ATTRIBUTESENTRY']._loaded_options = None + _globals['_DEPLOYAGENTV2REQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DEPLOYAGENTV2REQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_DEPLOYAGENTV2REQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_DEPLOYAGENTV2RESPONSE'].fields_by_name['agent_id']._loaded_options = None + _globals['_DEPLOYAGENTV2RESPONSE'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_UPDATEAGENTSECRETSREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_UPDATEAGENTSECRETSREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_ROLLBACKAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_ROLLBACKAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_DELETEAGENTREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_DELETEAGENTREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_LISTAGENTSECRETSREQUEST'].fields_by_name['agent_id']._loaded_options = None + _globals['_LISTAGENTSECRETSREQUEST'].fields_by_name['agent_id']._serialized_options = b'\272P\007agentID' + _globals['_PRIVATELINK_AWSCONFIG'].fields_by_name['endpoint']._loaded_options = None + _globals['_PRIVATELINK_AWSCONFIG'].fields_by_name['endpoint']._serialized_options = b'\030\001' + _globals['_PRIVATELINK'].fields_by_name['private_link_id']._loaded_options = None + _globals['_PRIVATELINK'].fields_by_name['private_link_id']._serialized_options = b'\272P\rprivateLinkID' + _globals['_PRIVATELINK'].fields_by_name['aws']._loaded_options = None + _globals['_PRIVATELINK'].fields_by_name['aws']._serialized_options = b'\030\001' + _globals['_CREATEPRIVATELINKREQUEST_AWSCREATECONFIG'].fields_by_name['endpoint']._loaded_options = None + _globals['_CREATEPRIVATELINKREQUEST_AWSCREATECONFIG'].fields_by_name['endpoint']._serialized_options = b'\030\001' + _globals['_CREATEPRIVATELINKREQUEST'].fields_by_name['aws']._loaded_options = None + _globals['_CREATEPRIVATELINKREQUEST'].fields_by_name['aws']._serialized_options = b'\030\001' + _globals['_DESTROYPRIVATELINKREQUEST'].fields_by_name['private_link_id']._loaded_options = None + _globals['_DESTROYPRIVATELINKREQUEST'].fields_by_name['private_link_id']._serialized_options = b'\272P\rprivateLinkID' + _globals['_GETPRIVATELINKSTATUSREQUEST'].fields_by_name['private_link_id']._loaded_options = None + _globals['_GETPRIVATELINKSTATUSREQUEST'].fields_by_name['private_link_id']._serialized_options = b'\272P\rprivateLinkID' + _globals['_AGENTSECRETKIND']._serialized_start=6072 + _globals['_AGENTSECRETKIND']._serialized_end=6183 + _globals['_AGENTEVENTTYPE']._serialized_start=6186 + _globals['_AGENTEVENTTYPE']._serialized_end=6372 + _globals['_AGENTEVENT']._serialized_start=93 + _globals['_AGENTEVENT']._serialized_end=206 + _globals['_AGENTSECRET']._serialized_start=209 + _globals['_AGENTSECRET']._serialized_end=408 + _globals['_CREATEAGENTREQUEST']._serialized_start=411 + _globals['_CREATEAGENTREQUEST']._serialized_end=580 + _globals['_CREATEAGENTRESPONSE']._serialized_start=583 + _globals['_CREATEAGENTRESPONSE']._serialized_end=810 + _globals['_CREATEAGENTV2REQUEST']._serialized_start=812 + _globals['_CREATEAGENTV2REQUEST']._serialized_end=890 + _globals['_CREATEAGENTV2RESPONSE']._serialized_start=892 + _globals['_CREATEAGENTV2RESPONSE']._serialized_end=985 + _globals['_PROMOTEAGENTREQUEST']._serialized_start=987 + _globals['_PROMOTEAGENTREQUEST']._serialized_end=1086 + _globals['_PROMOTEAGENTRESPONSE']._serialized_start=1089 + _globals['_PROMOTEAGENTRESPONSE']._serialized_end=1244 + _globals['_PRESIGNEDPOSTREQUEST']._serialized_start=1247 + _globals['_PRESIGNEDPOSTREQUEST']._serialized_end=1388 + _globals['_PRESIGNEDPOSTREQUEST_VALUESENTRY']._serialized_start=1343 + _globals['_PRESIGNEDPOSTREQUEST_VALUESENTRY']._serialized_end=1388 + _globals['_AGENTDEPLOYMENT']._serialized_start=1391 + _globals['_AGENTDEPLOYMENT']._serialized_end=1836 + _globals['_AGENTINFO']._serialized_start=1839 + _globals['_AGENTINFO']._serialized_end=2058 + _globals['_LISTAGENTSREQUEST']._serialized_start=2060 + _globals['_LISTAGENTSREQUEST']._serialized_end=2129 + _globals['_LISTAGENTSRESPONSE']._serialized_start=2131 + _globals['_LISTAGENTSRESPONSE']._serialized_end=2187 + _globals['_AGENTVERSION']._serialized_start=2190 + _globals['_AGENTVERSION']._serialized_end=2510 + _globals['_AGENTVERSION_ATTRIBUTESENTRY']._serialized_start=2461 + _globals['_AGENTVERSION_ATTRIBUTESENTRY']._serialized_end=2510 + _globals['_LISTAGENTVERSIONSREQUEST']._serialized_start=2512 + _globals['_LISTAGENTVERSIONSREQUEST']._serialized_end=2588 + _globals['_LISTAGENTVERSIONSRESPONSE']._serialized_start=2590 + _globals['_LISTAGENTVERSIONSRESPONSE']._serialized_end=2658 + _globals['_UPDATEAGENTREQUEST']._serialized_start=2661 + _globals['_UPDATEAGENTREQUEST']._serialized_end=2860 + _globals['_UPDATEAGENTRESPONSE']._serialized_start=2862 + _globals['_UPDATEAGENTRESPONSE']._serialized_end=2917 + _globals['_RESTARTAGENTREQUEST']._serialized_start=2919 + _globals['_RESTARTAGENTREQUEST']._serialized_end=2970 + _globals['_RESTARTAGENTRESPONSE']._serialized_start=2972 + _globals['_RESTARTAGENTRESPONSE']._serialized_end=3028 + _globals['_DEPLOYAGENTREQUEST']._serialized_start=3031 + _globals['_DEPLOYAGENTREQUEST']._serialized_end=3349 + _globals['_DEPLOYAGENTREQUEST_ATTRIBUTESENTRY']._serialized_start=2461 + _globals['_DEPLOYAGENTREQUEST_ATTRIBUTESENTRY']._serialized_end=2510 + _globals['_DEPLOYAGENTRESPONSE']._serialized_start=3352 + _globals['_DEPLOYAGENTRESPONSE']._serialized_end=3560 + _globals['_DEPLOYAGENTV2REQUEST']._serialized_start=3563 + _globals['_DEPLOYAGENTV2REQUEST']._serialized_end=3792 + _globals['_DEPLOYAGENTV2REQUEST_ATTRIBUTESENTRY']._serialized_start=2461 + _globals['_DEPLOYAGENTV2REQUEST_ATTRIBUTESENTRY']._serialized_end=2510 + _globals['_DEPLOYAGENTV2RESPONSE']._serialized_start=3795 + _globals['_DEPLOYAGENTV2RESPONSE']._serialized_end=3969 + _globals['_UPDATEAGENTSECRETSREQUEST']._serialized_start=3972 + _globals['_UPDATEAGENTSECRETSREQUEST']._serialized_end=4123 + _globals['_UPDATEAGENTSECRETSRESPONSE']._serialized_start=4125 + _globals['_UPDATEAGENTSECRETSRESPONSE']._serialized_end=4187 + _globals['_ROLLBACKAGENTREQUEST']._serialized_start=4189 + _globals['_ROLLBACKAGENTREQUEST']._serialized_end=4278 + _globals['_ROLLBACKAGENTRESPONSE']._serialized_start=4280 + _globals['_ROLLBACKAGENTRESPONSE']._serialized_end=4337 + _globals['_DELETEAGENTREQUEST']._serialized_start=4339 + _globals['_DELETEAGENTREQUEST']._serialized_end=4429 + _globals['_DELETEAGENTRESPONSE']._serialized_start=4431 + _globals['_DELETEAGENTRESPONSE']._serialized_end=4486 + _globals['_LISTAGENTSECRETSREQUEST']._serialized_start=4488 + _globals['_LISTAGENTSECRETSREQUEST']._serialized_end=4563 + _globals['_LISTAGENTSECRETSRESPONSE']._serialized_start=4565 + _globals['_LISTAGENTSECRETSRESPONSE']._serialized_end=4630 + _globals['_SETTINGSPARAM']._serialized_start=4632 + _globals['_SETTINGSPARAM']._serialized_end=4676 + _globals['_CLIENTSETTINGSRESPONSE']._serialized_start=4678 + _globals['_CLIENTSETTINGSRESPONSE']._serialized_end=4742 + _globals['_CLIENTSETTINGSREQUEST']._serialized_start=4744 + _globals['_CLIENTSETTINGSREQUEST']._serialized_end=4767 + _globals['_PRIVATELINK']._serialized_start=4770 + _globals['_PRIVATELINK']._serialized_end=5035 + _globals['_PRIVATELINK_AWSCONFIG']._serialized_start=4992 + _globals['_PRIVATELINK_AWSCONFIG']._serialized_end=5025 + _globals['_PRIVATELINKSTATUS']._serialized_start=5038 + _globals['_PRIVATELINKSTATUS']._serialized_end=5398 + _globals['_PRIVATELINKSTATUS_STATUS']._serialized_start=5175 + _globals['_PRIVATELINKSTATUS_STATUS']._serialized_end=5398 + _globals['_CREATEPRIVATELINKREQUEST']._serialized_start=5401 + _globals['_CREATEPRIVATELINKREQUEST']._serialized_end=5654 + _globals['_CREATEPRIVATELINKREQUEST_AWSCREATECONFIG']._serialized_start=5588 + _globals['_CREATEPRIVATELINKREQUEST_AWSCREATECONFIG']._serialized_end=5627 + _globals['_CREATEPRIVATELINKRESPONSE']._serialized_start=5656 + _globals['_CREATEPRIVATELINKRESPONSE']._serialized_end=5727 + _globals['_DESTROYPRIVATELINKREQUEST']._serialized_start=5729 + _globals['_DESTROYPRIVATELINKREQUEST']._serialized_end=5799 + _globals['_DESTROYPRIVATELINKRESPONSE']._serialized_start=5801 + _globals['_DESTROYPRIVATELINKRESPONSE']._serialized_end=5829 + _globals['_LISTPRIVATELINKSREQUEST']._serialized_start=5831 + _globals['_LISTPRIVATELINKSREQUEST']._serialized_end=5856 + _globals['_LISTPRIVATELINKSRESPONSE']._serialized_start=5858 + _globals['_LISTPRIVATELINKSRESPONSE']._serialized_end=5921 + _globals['_GETPRIVATELINKSTATUSREQUEST']._serialized_start=5923 + _globals['_GETPRIVATELINKSTATUSREQUEST']._serialized_end=5995 + _globals['_GETPRIVATELINKSTATUSRESPONSE']._serialized_start=5997 + _globals['_GETPRIVATELINKSTATUSRESPONSE']._serialized_end=6070 + _globals['_CLOUDAGENT']._serialized_start=6375 + _globals['_CLOUDAGENT']._serialized_end=7923 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/cloud_agent.pyi b/livekit-protocol/livekit/protocol/cloud_agent.pyi new file mode 100644 index 00000000..df832290 --- /dev/null +++ b/livekit-protocol/livekit/protocol/cloud_agent.pyi @@ -0,0 +1,583 @@ +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class AgentSecretKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AGENT_SECRET_KIND_UNKNOWN: _ClassVar[AgentSecretKind] + AGENT_SECRET_KIND_ENVIRONMENT: _ClassVar[AgentSecretKind] + AGENT_SECRET_KIND_FILE: _ClassVar[AgentSecretKind] + +class AgentEventType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AGENT_EVENT_TYPE_UNKNOWN: _ClassVar[AgentEventType] + AGENT_EVENT_TYPE_APPLICATION_CRASHED: _ClassVar[AgentEventType] + AGENT_EVENT_TYPE_RESTARTED_HIGH_DISK_USAGE: _ClassVar[AgentEventType] + AGENT_EVENT_TYPE_RESTARTED_HIGH_MEMORY_USAGE: _ClassVar[AgentEventType] +AGENT_SECRET_KIND_UNKNOWN: AgentSecretKind +AGENT_SECRET_KIND_ENVIRONMENT: AgentSecretKind +AGENT_SECRET_KIND_FILE: AgentSecretKind +AGENT_EVENT_TYPE_UNKNOWN: AgentEventType +AGENT_EVENT_TYPE_APPLICATION_CRASHED: AgentEventType +AGENT_EVENT_TYPE_RESTARTED_HIGH_DISK_USAGE: AgentEventType +AGENT_EVENT_TYPE_RESTARTED_HIGH_MEMORY_USAGE: AgentEventType + +class AgentEvent(_message.Message): + __slots__ = ("type", "count", "timestamp") + TYPE_FIELD_NUMBER: _ClassVar[int] + COUNT_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + type: AgentEventType + count: int + timestamp: _timestamp_pb2.Timestamp + def __init__(self, type: _Optional[_Union[AgentEventType, str]] = ..., count: _Optional[int] = ..., timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class AgentSecret(_message.Message): + __slots__ = ("name", "value", "created_at", "updated_at", "kind", "deployments") + NAME_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] + KIND_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENTS_FIELD_NUMBER: _ClassVar[int] + name: str + value: bytes + created_at: _timestamp_pb2.Timestamp + updated_at: _timestamp_pb2.Timestamp + kind: AgentSecretKind + deployments: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, name: _Optional[str] = ..., value: _Optional[bytes] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., kind: _Optional[_Union[AgentSecretKind, str]] = ..., deployments: _Optional[_Iterable[str]] = ...) -> None: ... + +class CreateAgentRequest(_message.Message): + __slots__ = ("agent_name", "secrets", "replicas", "max_replicas", "cpu_req", "regions") + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + REPLICAS_FIELD_NUMBER: _ClassVar[int] + MAX_REPLICAS_FIELD_NUMBER: _ClassVar[int] + CPU_REQ_FIELD_NUMBER: _ClassVar[int] + REGIONS_FIELD_NUMBER: _ClassVar[int] + agent_name: str + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + replicas: int + max_replicas: int + cpu_req: str + regions: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, agent_name: _Optional[str] = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., replicas: _Optional[int] = ..., max_replicas: _Optional[int] = ..., cpu_req: _Optional[str] = ..., regions: _Optional[_Iterable[str]] = ...) -> None: ... + +class CreateAgentResponse(_message.Message): + __slots__ = ("agent_id", "agent_name", "status", "version", "presigned_url", "tag", "server_regions", "presigned_post_request") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_URL_FIELD_NUMBER: _ClassVar[int] + TAG_FIELD_NUMBER: _ClassVar[int] + SERVER_REGIONS_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_POST_REQUEST_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + status: str + version: str + presigned_url: str + tag: str + server_regions: _containers.RepeatedScalarFieldContainer[str] + presigned_post_request: PresignedPostRequest + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., status: _Optional[str] = ..., version: _Optional[str] = ..., presigned_url: _Optional[str] = ..., tag: _Optional[str] = ..., server_regions: _Optional[_Iterable[str]] = ..., presigned_post_request: _Optional[_Union[PresignedPostRequest, _Mapping]] = ...) -> None: ... + +class CreateAgentV2Request(_message.Message): + __slots__ = ("secrets", "regions") + SECRETS_FIELD_NUMBER: _ClassVar[int] + REGIONS_FIELD_NUMBER: _ClassVar[int] + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + regions: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., regions: _Optional[_Iterable[str]] = ...) -> None: ... + +class CreateAgentV2Response(_message.Message): + __slots__ = ("agent_id", "status", "server_regions") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + SERVER_REGIONS_FIELD_NUMBER: _ClassVar[int] + agent_id: str + status: str + server_regions: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, agent_id: _Optional[str] = ..., status: _Optional[str] = ..., server_regions: _Optional[_Iterable[str]] = ...) -> None: ... + +class PromoteAgentRequest(_message.Message): + __slots__ = ("agent_id", "src_deployment", "dst_deployment") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + SRC_DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + DST_DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + agent_id: str + src_deployment: str + dst_deployment: str + def __init__(self, agent_id: _Optional[str] = ..., src_deployment: _Optional[str] = ..., dst_deployment: _Optional[str] = ...) -> None: ... + +class PromoteAgentResponse(_message.Message): + __slots__ = ("success", "message", "agent_id", "src_deployment", "dst_deployment", "version_tag") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + SRC_DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + DST_DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + VERSION_TAG_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + agent_id: str + src_deployment: str + dst_deployment: str + version_tag: str + def __init__(self, success: bool = ..., message: _Optional[str] = ..., agent_id: _Optional[str] = ..., src_deployment: _Optional[str] = ..., dst_deployment: _Optional[str] = ..., version_tag: _Optional[str] = ...) -> None: ... + +class PresignedPostRequest(_message.Message): + __slots__ = ("url", "values") + class ValuesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + URL_FIELD_NUMBER: _ClassVar[int] + VALUES_FIELD_NUMBER: _ClassVar[int] + url: str + values: _containers.ScalarMap[str, str] + def __init__(self, url: _Optional[str] = ..., values: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class AgentDeployment(_message.Message): + __slots__ = ("region", "agent_id", "status", "replicas", "min_replicas", "max_replicas", "cpu_req", "cur_cpu", "cur_mem", "mem_req", "mem_limit", "cpu_limit", "server_region", "events", "deployment", "version", "agent_name", "deployment_enabled", "last_scraped_at") + REGION_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + REPLICAS_FIELD_NUMBER: _ClassVar[int] + MIN_REPLICAS_FIELD_NUMBER: _ClassVar[int] + MAX_REPLICAS_FIELD_NUMBER: _ClassVar[int] + CPU_REQ_FIELD_NUMBER: _ClassVar[int] + CUR_CPU_FIELD_NUMBER: _ClassVar[int] + CUR_MEM_FIELD_NUMBER: _ClassVar[int] + MEM_REQ_FIELD_NUMBER: _ClassVar[int] + MEM_LIMIT_FIELD_NUMBER: _ClassVar[int] + CPU_LIMIT_FIELD_NUMBER: _ClassVar[int] + SERVER_REGION_FIELD_NUMBER: _ClassVar[int] + EVENTS_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_ENABLED_FIELD_NUMBER: _ClassVar[int] + LAST_SCRAPED_AT_FIELD_NUMBER: _ClassVar[int] + region: str + agent_id: str + status: str + replicas: int + min_replicas: int + max_replicas: int + cpu_req: str + cur_cpu: str + cur_mem: str + mem_req: str + mem_limit: str + cpu_limit: str + server_region: str + events: _containers.RepeatedCompositeFieldContainer[AgentEvent] + deployment: str + version: str + agent_name: str + deployment_enabled: bool + last_scraped_at: _timestamp_pb2.Timestamp + def __init__(self, region: _Optional[str] = ..., agent_id: _Optional[str] = ..., status: _Optional[str] = ..., replicas: _Optional[int] = ..., min_replicas: _Optional[int] = ..., max_replicas: _Optional[int] = ..., cpu_req: _Optional[str] = ..., cur_cpu: _Optional[str] = ..., cur_mem: _Optional[str] = ..., mem_req: _Optional[str] = ..., mem_limit: _Optional[str] = ..., cpu_limit: _Optional[str] = ..., server_region: _Optional[str] = ..., events: _Optional[_Iterable[_Union[AgentEvent, _Mapping]]] = ..., deployment: _Optional[str] = ..., version: _Optional[str] = ..., agent_name: _Optional[str] = ..., deployment_enabled: bool = ..., last_scraped_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class AgentInfo(_message.Message): + __slots__ = ("agent_id", "agent_name", "version", "agent_deployments", "secrets", "deployed_at") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + AGENT_DEPLOYMENTS_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + DEPLOYED_AT_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + version: str + agent_deployments: _containers.RepeatedCompositeFieldContainer[AgentDeployment] + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + deployed_at: _timestamp_pb2.Timestamp + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., version: _Optional[str] = ..., agent_deployments: _Optional[_Iterable[_Union[AgentDeployment, _Mapping]]] = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., deployed_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class ListAgentsRequest(_message.Message): + __slots__ = ("agent_name", "agent_id") + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + agent_name: str + agent_id: str + def __init__(self, agent_name: _Optional[str] = ..., agent_id: _Optional[str] = ...) -> None: ... + +class ListAgentsResponse(_message.Message): + __slots__ = ("agents",) + AGENTS_FIELD_NUMBER: _ClassVar[int] + agents: _containers.RepeatedCompositeFieldContainer[AgentInfo] + def __init__(self, agents: _Optional[_Iterable[_Union[AgentInfo, _Mapping]]] = ...) -> None: ... + +class AgentVersion(_message.Message): + __slots__ = ("version", "current", "created_at", "deployed_at", "attributes", "status", "owner", "draining", "active") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + VERSION_FIELD_NUMBER: _ClassVar[int] + CURRENT_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + DEPLOYED_AT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + OWNER_FIELD_NUMBER: _ClassVar[int] + DRAINING_FIELD_NUMBER: _ClassVar[int] + ACTIVE_FIELD_NUMBER: _ClassVar[int] + version: str + current: bool + created_at: _timestamp_pb2.Timestamp + deployed_at: _timestamp_pb2.Timestamp + attributes: _containers.ScalarMap[str, str] + status: str + owner: str + draining: bool + active: bool + def __init__(self, version: _Optional[str] = ..., current: bool = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., deployed_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., attributes: _Optional[_Mapping[str, str]] = ..., status: _Optional[str] = ..., owner: _Optional[str] = ..., draining: bool = ..., active: bool = ...) -> None: ... + +class ListAgentVersionsRequest(_message.Message): + __slots__ = ("agent_id", "agent_name") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ...) -> None: ... + +class ListAgentVersionsResponse(_message.Message): + __slots__ = ("versions",) + VERSIONS_FIELD_NUMBER: _ClassVar[int] + versions: _containers.RepeatedCompositeFieldContainer[AgentVersion] + def __init__(self, versions: _Optional[_Iterable[_Union[AgentVersion, _Mapping]]] = ...) -> None: ... + +class UpdateAgentRequest(_message.Message): + __slots__ = ("agent_id", "agent_name", "replicas", "max_replicas", "cpu_req", "regions", "secrets") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + REPLICAS_FIELD_NUMBER: _ClassVar[int] + MAX_REPLICAS_FIELD_NUMBER: _ClassVar[int] + CPU_REQ_FIELD_NUMBER: _ClassVar[int] + REGIONS_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + replicas: int + max_replicas: int + cpu_req: str + regions: _containers.RepeatedScalarFieldContainer[str] + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., replicas: _Optional[int] = ..., max_replicas: _Optional[int] = ..., cpu_req: _Optional[str] = ..., regions: _Optional[_Iterable[str]] = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ...) -> None: ... + +class UpdateAgentResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class RestartAgentRequest(_message.Message): + __slots__ = ("agent_id",) + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + agent_id: str + def __init__(self, agent_id: _Optional[str] = ...) -> None: ... + +class RestartAgentResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class DeployAgentRequest(_message.Message): + __slots__ = ("agent_id", "agent_name", "secrets", "replicas", "max_replicas", "cpu_req", "deployment", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + REPLICAS_FIELD_NUMBER: _ClassVar[int] + MAX_REPLICAS_FIELD_NUMBER: _ClassVar[int] + CPU_REQ_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + replicas: int + max_replicas: int + cpu_req: str + deployment: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., replicas: _Optional[int] = ..., max_replicas: _Optional[int] = ..., cpu_req: _Optional[str] = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class DeployAgentResponse(_message.Message): + __slots__ = ("success", "message", "agent_id", "presigned_url", "tag", "presigned_post_request", "deployment") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_URL_FIELD_NUMBER: _ClassVar[int] + TAG_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_POST_REQUEST_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + agent_id: str + presigned_url: str + tag: str + presigned_post_request: PresignedPostRequest + deployment: str + def __init__(self, success: bool = ..., message: _Optional[str] = ..., agent_id: _Optional[str] = ..., presigned_url: _Optional[str] = ..., tag: _Optional[str] = ..., presigned_post_request: _Optional[_Union[PresignedPostRequest, _Mapping]] = ..., deployment: _Optional[str] = ...) -> None: ... + +class DeployAgentV2Request(_message.Message): + __slots__ = ("agent_id", "secrets", "deployment", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + agent_id: str + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + deployment: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, agent_id: _Optional[str] = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., deployment: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class DeployAgentV2Response(_message.Message): + __slots__ = ("success", "message", "agent_id", "tag", "presigned_req", "deployment") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + TAG_FIELD_NUMBER: _ClassVar[int] + PRESIGNED_REQ_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + agent_id: str + tag: str + presigned_req: PresignedPostRequest + deployment: str + def __init__(self, success: bool = ..., message: _Optional[str] = ..., agent_id: _Optional[str] = ..., tag: _Optional[str] = ..., presigned_req: _Optional[_Union[PresignedPostRequest, _Mapping]] = ..., deployment: _Optional[str] = ...) -> None: ... + +class UpdateAgentSecretsRequest(_message.Message): + __slots__ = ("agent_id", "agent_name", "overwrite", "secrets", "remove") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + OVERWRITE_FIELD_NUMBER: _ClassVar[int] + SECRETS_FIELD_NUMBER: _ClassVar[int] + REMOVE_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + overwrite: bool + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + remove: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., overwrite: bool = ..., secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ..., remove: _Optional[_Iterable[str]] = ...) -> None: ... + +class UpdateAgentSecretsResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class RollbackAgentRequest(_message.Message): + __slots__ = ("agent_id", "agent_name", "version") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + version: str + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., version: _Optional[str] = ...) -> None: ... + +class RollbackAgentResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class DeleteAgentRequest(_message.Message): + __slots__ = ("agent_id", "agent_name", "deployment") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + DEPLOYMENT_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + deployment: str + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ..., deployment: _Optional[str] = ...) -> None: ... + +class DeleteAgentResponse(_message.Message): + __slots__ = ("success", "message") + SUCCESS_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + success: bool + message: str + def __init__(self, success: bool = ..., message: _Optional[str] = ...) -> None: ... + +class ListAgentSecretsRequest(_message.Message): + __slots__ = ("agent_id", "agent_name") + AGENT_ID_FIELD_NUMBER: _ClassVar[int] + AGENT_NAME_FIELD_NUMBER: _ClassVar[int] + agent_id: str + agent_name: str + def __init__(self, agent_id: _Optional[str] = ..., agent_name: _Optional[str] = ...) -> None: ... + +class ListAgentSecretsResponse(_message.Message): + __slots__ = ("secrets",) + SECRETS_FIELD_NUMBER: _ClassVar[int] + secrets: _containers.RepeatedCompositeFieldContainer[AgentSecret] + def __init__(self, secrets: _Optional[_Iterable[_Union[AgentSecret, _Mapping]]] = ...) -> None: ... + +class SettingsParam(_message.Message): + __slots__ = ("name", "value") + NAME_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + name: str + value: str + def __init__(self, name: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + +class ClientSettingsResponse(_message.Message): + __slots__ = ("params",) + PARAMS_FIELD_NUMBER: _ClassVar[int] + params: _containers.RepeatedCompositeFieldContainer[SettingsParam] + def __init__(self, params: _Optional[_Iterable[_Union[SettingsParam, _Mapping]]] = ...) -> None: ... + +class ClientSettingsRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class PrivateLink(_message.Message): + __slots__ = ("private_link_id", "name", "region", "port", "endpoint", "connection_endpoint", "cloud_region", "aws") + class AWSConfig(_message.Message): + __slots__ = ("endpoint",) + ENDPOINT_FIELD_NUMBER: _ClassVar[int] + endpoint: str + def __init__(self, endpoint: _Optional[str] = ...) -> None: ... + PRIVATE_LINK_ID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + PORT_FIELD_NUMBER: _ClassVar[int] + ENDPOINT_FIELD_NUMBER: _ClassVar[int] + CONNECTION_ENDPOINT_FIELD_NUMBER: _ClassVar[int] + CLOUD_REGION_FIELD_NUMBER: _ClassVar[int] + AWS_FIELD_NUMBER: _ClassVar[int] + private_link_id: str + name: str + region: str + port: int + endpoint: str + connection_endpoint: str + cloud_region: str + aws: PrivateLink.AWSConfig + def __init__(self, private_link_id: _Optional[str] = ..., name: _Optional[str] = ..., region: _Optional[str] = ..., port: _Optional[int] = ..., endpoint: _Optional[str] = ..., connection_endpoint: _Optional[str] = ..., cloud_region: _Optional[str] = ..., aws: _Optional[_Union[PrivateLink.AWSConfig, _Mapping]] = ...) -> None: ... + +class PrivateLinkStatus(_message.Message): + __slots__ = ("status", "updated_at", "reason") + class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PRIVATE_LINK_STATUS_UNKNOWN: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_PROVISIONING: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_PENDING_APPROVAL: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_HEALTHY: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_UNHEALTHY: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_APPROVED: _ClassVar[PrivateLinkStatus.Status] + PRIVATE_LINK_STATUS_UNKNOWN: PrivateLinkStatus.Status + PRIVATE_LINK_STATUS_PROVISIONING: PrivateLinkStatus.Status + PRIVATE_LINK_STATUS_PENDING_APPROVAL: PrivateLinkStatus.Status + PRIVATE_LINK_STATUS_HEALTHY: PrivateLinkStatus.Status + PRIVATE_LINK_STATUS_UNHEALTHY: PrivateLinkStatus.Status + PRIVATE_LINK_STATUS_APPROVED: PrivateLinkStatus.Status + STATUS_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + status: PrivateLinkStatus.Status + updated_at: _timestamp_pb2.Timestamp + reason: str + def __init__(self, status: _Optional[_Union[PrivateLinkStatus.Status, str]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., reason: _Optional[str] = ...) -> None: ... + +class CreatePrivateLinkRequest(_message.Message): + __slots__ = ("name", "region", "port", "endpoint", "cloud_region", "aws") + class AWSCreateConfig(_message.Message): + __slots__ = ("endpoint",) + ENDPOINT_FIELD_NUMBER: _ClassVar[int] + endpoint: str + def __init__(self, endpoint: _Optional[str] = ...) -> None: ... + NAME_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + PORT_FIELD_NUMBER: _ClassVar[int] + ENDPOINT_FIELD_NUMBER: _ClassVar[int] + CLOUD_REGION_FIELD_NUMBER: _ClassVar[int] + AWS_FIELD_NUMBER: _ClassVar[int] + name: str + region: str + port: int + endpoint: str + cloud_region: str + aws: CreatePrivateLinkRequest.AWSCreateConfig + def __init__(self, name: _Optional[str] = ..., region: _Optional[str] = ..., port: _Optional[int] = ..., endpoint: _Optional[str] = ..., cloud_region: _Optional[str] = ..., aws: _Optional[_Union[CreatePrivateLinkRequest.AWSCreateConfig, _Mapping]] = ...) -> None: ... + +class CreatePrivateLinkResponse(_message.Message): + __slots__ = ("private_link",) + PRIVATE_LINK_FIELD_NUMBER: _ClassVar[int] + private_link: PrivateLink + def __init__(self, private_link: _Optional[_Union[PrivateLink, _Mapping]] = ...) -> None: ... + +class DestroyPrivateLinkRequest(_message.Message): + __slots__ = ("private_link_id",) + PRIVATE_LINK_ID_FIELD_NUMBER: _ClassVar[int] + private_link_id: str + def __init__(self, private_link_id: _Optional[str] = ...) -> None: ... + +class DestroyPrivateLinkResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ListPrivateLinksRequest(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ListPrivateLinksResponse(_message.Message): + __slots__ = ("items",) + ITEMS_FIELD_NUMBER: _ClassVar[int] + items: _containers.RepeatedCompositeFieldContainer[PrivateLink] + def __init__(self, items: _Optional[_Iterable[_Union[PrivateLink, _Mapping]]] = ...) -> None: ... + +class GetPrivateLinkStatusRequest(_message.Message): + __slots__ = ("private_link_id",) + PRIVATE_LINK_ID_FIELD_NUMBER: _ClassVar[int] + private_link_id: str + def __init__(self, private_link_id: _Optional[str] = ...) -> None: ... + +class GetPrivateLinkStatusResponse(_message.Message): + __slots__ = ("value",) + VALUE_FIELD_NUMBER: _ClassVar[int] + value: PrivateLinkStatus + def __init__(self, value: _Optional[_Union[PrivateLinkStatus, _Mapping]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/connector.py b/livekit-protocol/livekit/protocol/connector.py new file mode 100644 index 00000000..0c188bd1 --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_connector.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_connector.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import connector_whatsapp as _connector__whatsapp_ +from . import connector_twilio as _connector__twilio_ + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17livekit_connector.proto\x12\x07livekit\x1a livekit_connector_whatsapp.proto\x1a\x1elivekit_connector_twilio.proto*:\n\rConnectorType\x12\x0f\n\x0bUnspecified\x10\x00\x12\x0c\n\x08WhatsApp\x10\x01\x12\n\n\x06Twilio\x10\x02\x32\xec\x03\n\tConnector\x12W\n\x10\x44ialWhatsAppCall\x12 .livekit.DialWhatsAppCallRequest\x1a!.livekit.DialWhatsAppCallResponse\x12i\n\x16\x44isconnectWhatsAppCall\x12&.livekit.DisconnectWhatsAppCallRequest\x1a\'.livekit.DisconnectWhatsAppCallResponse\x12`\n\x13\x43onnectWhatsAppCall\x12#.livekit.ConnectWhatsAppCallRequest\x1a$.livekit.ConnectWhatsAppCallResponse\x12]\n\x12\x41\x63\x63\x65ptWhatsAppCall\x12\".livekit.AcceptWhatsAppCallRequest\x1a#.livekit.AcceptWhatsAppCallResponse\x12Z\n\x11\x43onnectTwilioCall\x12!.livekit.ConnectTwilioCallRequest\x1a\".livekit.ConnectTwilioCallResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'connector', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_CONNECTORTYPE']._serialized_start=102 + _globals['_CONNECTORTYPE']._serialized_end=160 + _globals['_CONNECTOR']._serialized_start=163 + _globals['_CONNECTOR']._serialized_end=655 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/connector.pyi b/livekit-protocol/livekit/protocol/connector.pyi new file mode 100644 index 00000000..98459956 --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector.pyi @@ -0,0 +1,16 @@ +from . import connector_whatsapp as _connector_whatsapp +from . import connector_twilio as _connector_twilio +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class ConnectorType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + Unspecified: _ClassVar[ConnectorType] + WhatsApp: _ClassVar[ConnectorType] + Twilio: _ClassVar[ConnectorType] +Unspecified: ConnectorType +WhatsApp: ConnectorType +Twilio: ConnectorType diff --git a/livekit-protocol/livekit/protocol/connector_twilio.py b/livekit-protocol/livekit/protocol/connector_twilio.py new file mode 100644 index 00000000..64a4a18d --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector_twilio.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_connector_twilio.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_connector_twilio.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import agent_dispatch as _agent__dispatch_ +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1elivekit_connector_twilio.proto\x12\x07livekit\x1a\x1clivekit_agent_dispatch.proto\x1a\x14logger/options.proto\"\x8f\x05\n\x18\x43onnectTwilioCallRequest\x12T\n\x15twilio_call_direction\x18\x01 \x01(\x0e\x32\x35.livekit.ConnectTwilioCallRequest.TwilioCallDirection\x12\x11\n\troom_name\x18\x02 \x01(\t\x12*\n\x06\x61gents\x18\x03 \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12>\n\x10participant_name\x18\x05 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x42\n\x14participant_metadata\x18\x06 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x82\x01\n\x16participant_attributes\x18\x07 \x03(\x0b\x32<.livekit.ConnectTwilioCallRequest.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x1b\n\x13\x64\x65stination_country\x18\x08 \x01(\t\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\\\n\x13TwilioCallDirection\x12!\n\x1dTWILIO_CALL_DIRECTION_INBOUND\x10\x00\x12\"\n\x1eTWILIO_CALL_DIRECTION_OUTBOUND\x10\x01\"0\n\x19\x43onnectTwilioCallResponse\x12\x13\n\x0b\x63onnect_url\x18\x01 \x01(\tBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'connector_twilio', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_CONNECTTWILIOCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_CONNECTTWILIOCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_name']._serialized_options = b'\262P\036\300P\001' + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_CONNECTTWILIOCALLREQUEST'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_CONNECTTWILIOCALLREQUEST']._serialized_start=96 + _globals['_CONNECTTWILIOCALLREQUEST']._serialized_end=751 + _globals['_CONNECTTWILIOCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=597 + _globals['_CONNECTTWILIOCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=657 + _globals['_CONNECTTWILIOCALLREQUEST_TWILIOCALLDIRECTION']._serialized_start=659 + _globals['_CONNECTTWILIOCALLREQUEST_TWILIOCALLDIRECTION']._serialized_end=751 + _globals['_CONNECTTWILIOCALLRESPONSE']._serialized_start=753 + _globals['_CONNECTTWILIOCALLRESPONSE']._serialized_end=801 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/connector_twilio.pyi b/livekit-protocol/livekit/protocol/connector_twilio.pyi new file mode 100644 index 00000000..abf53ec8 --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector_twilio.pyi @@ -0,0 +1,48 @@ +from . import agent_dispatch as _agent_dispatch +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class ConnectTwilioCallRequest(_message.Message): + __slots__ = ("twilio_call_direction", "room_name", "agents", "participant_identity", "participant_name", "participant_metadata", "participant_attributes", "destination_country") + class TwilioCallDirection(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + TWILIO_CALL_DIRECTION_INBOUND: _ClassVar[ConnectTwilioCallRequest.TwilioCallDirection] + TWILIO_CALL_DIRECTION_OUTBOUND: _ClassVar[ConnectTwilioCallRequest.TwilioCallDirection] + TWILIO_CALL_DIRECTION_INBOUND: ConnectTwilioCallRequest.TwilioCallDirection + TWILIO_CALL_DIRECTION_OUTBOUND: ConnectTwilioCallRequest.TwilioCallDirection + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + TWILIO_CALL_DIRECTION_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + AGENTS_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_NAME_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_METADATA_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] + twilio_call_direction: ConnectTwilioCallRequest.TwilioCallDirection + room_name: str + agents: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + participant_identity: str + participant_name: str + participant_metadata: str + participant_attributes: _containers.ScalarMap[str, str] + destination_country: str + def __init__(self, twilio_call_direction: _Optional[_Union[ConnectTwilioCallRequest.TwilioCallDirection, str]] = ..., room_name: _Optional[str] = ..., agents: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., destination_country: _Optional[str] = ...) -> None: ... + +class ConnectTwilioCallResponse(_message.Message): + __slots__ = ("connect_url",) + CONNECT_URL_FIELD_NUMBER: _ClassVar[int] + connect_url: str + def __init__(self, connect_url: _Optional[str] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/connector_whatsapp.py b/livekit-protocol/livekit/protocol/connector_whatsapp.py new file mode 100644 index 00000000..d7d3916c --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector_whatsapp.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_connector_whatsapp.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_connector_whatsapp.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from . import agent_dispatch as _agent__dispatch_ +from . import rtc as _rtc_ +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n livekit_connector_whatsapp.proto\x12\x07livekit\x1a\x1egoogle/protobuf/duration.proto\x1a\x1clivekit_agent_dispatch.proto\x1a\x11livekit_rtc.proto\x1a\x14logger/options.proto\"\xde\x05\n\x17\x44ialWhatsAppCallRequest\x12:\n\x18whatsapp_phone_number_id\x18\x01 \x01(\tB\x18\xbaP\x15whatsappPhoneNumberID\x12%\n\x18whatsapp_to_phone_number\x18\x02 \x01(\tB\x03\xc0P\x01\x12\x1d\n\x10whatsapp_api_key\x18\x03 \x01(\tB\x03\xc0P\x02\x12\"\n\x1awhatsapp_cloud_api_version\x18\x0c \x01(\t\x12)\n!whatsapp_biz_opaque_callback_data\x18\x04 \x01(\t\x12\x11\n\troom_name\x18\x05 \x01(\t\x12*\n\x06\x61gents\x18\x06 \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\x12\x1c\n\x14participant_identity\x18\x07 \x01(\t\x12>\n\x10participant_name\x18\x08 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x42\n\x14participant_metadata\x18\t \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x81\x01\n\x16participant_attributes\x18\n \x03(\x0b\x32;.livekit.DialWhatsAppCallRequest.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x1b\n\x13\x64\x65stination_country\x18\x0b \x01(\t\x12\x32\n\x0fringing_timeout\x18\r \x01(\x0b\x32\x19.google.protobuf.Duration\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"Z\n\x18\x44ialWhatsAppCallResponse\x12+\n\x10whatsapp_call_id\x18\x01 \x01(\tB\x11\xbaP\x0ewhatsappCallID\x12\x11\n\troom_name\x18\x02 \x01(\t\"\xff\x01\n\x1d\x44isconnectWhatsAppCallRequest\x12+\n\x10whatsapp_call_id\x18\x01 \x01(\tB\x11\xbaP\x0ewhatsappCallID\x12\x1d\n\x10whatsapp_api_key\x18\x02 \x01(\tB\x03\xc0P\x02\x12R\n\x11\x64isconnect_reason\x18\x03 \x01(\x0e\x32\x37.livekit.DisconnectWhatsAppCallRequest.DisconnectReason\">\n\x10\x44isconnectReason\x12\x16\n\x12\x42USINESS_INITIATED\x10\x00\x12\x12\n\x0eUSER_INITIATED\x10\x01\" \n\x1e\x44isconnectWhatsAppCallResponse\"\x90\x01\n\x1a\x43onnectWhatsAppCallRequest\x12+\n\x10whatsapp_call_id\x18\x01 \x01(\tB\x11\xbaP\x0ewhatsappCallID\x12(\n\x03sdp\x18\x02 \x01(\x0b\x32\x1b.livekit.SessionDescription\x12\x1b\n\x13wait_until_answered\x18\x03 \x01(\x08\"\x1d\n\x1b\x43onnectWhatsAppCallResponse\"\xaf\x06\n\x19\x41\x63\x63\x65ptWhatsAppCallRequest\x12:\n\x18whatsapp_phone_number_id\x18\x01 \x01(\tB\x18\xbaP\x15whatsappPhoneNumberID\x12\x1d\n\x10whatsapp_api_key\x18\x02 \x01(\tB\x03\xc0P\x02\x12\"\n\x1awhatsapp_cloud_api_version\x18\r \x01(\t\x12+\n\x10whatsapp_call_id\x18\x03 \x01(\tB\x11\xbaP\x0ewhatsappCallID\x12)\n!whatsapp_biz_opaque_callback_data\x18\x04 \x01(\t\x12(\n\x03sdp\x18\x05 \x01(\x0b\x32\x1b.livekit.SessionDescription\x12\x11\n\troom_name\x18\x06 \x01(\t\x12*\n\x06\x61gents\x18\x07 \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\x12\x1c\n\x14participant_identity\x18\x08 \x01(\t\x12>\n\x10participant_name\x18\t \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x42\n\x14participant_metadata\x18\n \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x83\x01\n\x16participant_attributes\x18\x0b \x03(\x0b\x32=.livekit.AcceptWhatsAppCallRequest.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x1b\n\x13\x64\x65stination_country\x18\x0c \x01(\t\x12\x32\n\x0fringing_timeout\x18\x0e \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x1b\n\x13wait_until_answered\x18\x0f \x01(\x08\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"/\n\x1a\x41\x63\x63\x65ptWhatsAppCallResponse\x12\x11\n\troom_name\x18\x01 \x01(\t\"n\n\x0cWhatsAppCall\x12+\n\x10whatsapp_call_id\x18\x01 \x01(\tB\x11\xbaP\x0ewhatsappCallID\x12\x31\n\tdirection\x18\x02 \x01(\x0e\x32\x1e.livekit.WhatsAppCallDirection*b\n\x15WhatsAppCallDirection\x12#\n\x1fWHATSAPP_CALL_DIRECTION_INBOUND\x10\x00\x12$\n WHATSAPP_CALL_DIRECTION_OUTBOUND\x10\x02\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'connector_whatsapp', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_DIALWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_phone_number_id']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_phone_number_id']._serialized_options = b'\272P\025whatsappPhoneNumberID' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_to_phone_number']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_to_phone_number']._serialized_options = b'\300P\001' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._serialized_options = b'\300P\002' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_name']._serialized_options = b'\262P\036\300P\001' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_DIALWHATSAPPCALLREQUEST'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_DIALWHATSAPPCALLRESPONSE'].fields_by_name['whatsapp_call_id']._loaded_options = None + _globals['_DIALWHATSAPPCALLRESPONSE'].fields_by_name['whatsapp_call_id']._serialized_options = b'\272P\016whatsappCallID' + _globals['_DISCONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._loaded_options = None + _globals['_DISCONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._serialized_options = b'\272P\016whatsappCallID' + _globals['_DISCONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._loaded_options = None + _globals['_DISCONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._serialized_options = b'\300P\002' + _globals['_CONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._loaded_options = None + _globals['_CONNECTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._serialized_options = b'\272P\016whatsappCallID' + _globals['_ACCEPTWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_phone_number_id']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_phone_number_id']._serialized_options = b'\272P\025whatsappPhoneNumberID' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_api_key']._serialized_options = b'\300P\002' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['whatsapp_call_id']._serialized_options = b'\272P\016whatsappCallID' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_name']._serialized_options = b'\262P\036\300P\001' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_ACCEPTWHATSAPPCALLREQUEST'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_WHATSAPPCALL'].fields_by_name['whatsapp_call_id']._loaded_options = None + _globals['_WHATSAPPCALL'].fields_by_name['whatsapp_call_id']._serialized_options = b'\272P\016whatsappCallID' + _globals['_WHATSAPPCALLDIRECTION']._serialized_start=2426 + _globals['_WHATSAPPCALLDIRECTION']._serialized_end=2524 + _globals['_DIALWHATSAPPCALLREQUEST']._serialized_start=149 + _globals['_DIALWHATSAPPCALLREQUEST']._serialized_end=883 + _globals['_DIALWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=823 + _globals['_DIALWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=883 + _globals['_DIALWHATSAPPCALLRESPONSE']._serialized_start=885 + _globals['_DIALWHATSAPPCALLRESPONSE']._serialized_end=975 + _globals['_DISCONNECTWHATSAPPCALLREQUEST']._serialized_start=978 + _globals['_DISCONNECTWHATSAPPCALLREQUEST']._serialized_end=1233 + _globals['_DISCONNECTWHATSAPPCALLREQUEST_DISCONNECTREASON']._serialized_start=1171 + _globals['_DISCONNECTWHATSAPPCALLREQUEST_DISCONNECTREASON']._serialized_end=1233 + _globals['_DISCONNECTWHATSAPPCALLRESPONSE']._serialized_start=1235 + _globals['_DISCONNECTWHATSAPPCALLRESPONSE']._serialized_end=1267 + _globals['_CONNECTWHATSAPPCALLREQUEST']._serialized_start=1270 + _globals['_CONNECTWHATSAPPCALLREQUEST']._serialized_end=1414 + _globals['_CONNECTWHATSAPPCALLRESPONSE']._serialized_start=1416 + _globals['_CONNECTWHATSAPPCALLRESPONSE']._serialized_end=1445 + _globals['_ACCEPTWHATSAPPCALLREQUEST']._serialized_start=1448 + _globals['_ACCEPTWHATSAPPCALLREQUEST']._serialized_end=2263 + _globals['_ACCEPTWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=823 + _globals['_ACCEPTWHATSAPPCALLREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=883 + _globals['_ACCEPTWHATSAPPCALLRESPONSE']._serialized_start=2265 + _globals['_ACCEPTWHATSAPPCALLRESPONSE']._serialized_end=2312 + _globals['_WHATSAPPCALL']._serialized_start=2314 + _globals['_WHATSAPPCALL']._serialized_end=2424 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/connector_whatsapp.pyi b/livekit-protocol/livekit/protocol/connector_whatsapp.pyi new file mode 100644 index 00000000..2579fcf6 --- /dev/null +++ b/livekit-protocol/livekit/protocol/connector_whatsapp.pyi @@ -0,0 +1,152 @@ +from google.protobuf import duration_pb2 as _duration_pb2 +from . import agent_dispatch as _agent_dispatch +from . import rtc as _rtc +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class WhatsAppCallDirection(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + WHATSAPP_CALL_DIRECTION_INBOUND: _ClassVar[WhatsAppCallDirection] + WHATSAPP_CALL_DIRECTION_OUTBOUND: _ClassVar[WhatsAppCallDirection] +WHATSAPP_CALL_DIRECTION_INBOUND: WhatsAppCallDirection +WHATSAPP_CALL_DIRECTION_OUTBOUND: WhatsAppCallDirection + +class DialWhatsAppCallRequest(_message.Message): + __slots__ = ("whatsapp_phone_number_id", "whatsapp_to_phone_number", "whatsapp_api_key", "whatsapp_cloud_api_version", "whatsapp_biz_opaque_callback_data", "room_name", "agents", "participant_identity", "participant_name", "participant_metadata", "participant_attributes", "destination_country", "ringing_timeout") + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + WHATSAPP_PHONE_NUMBER_ID_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_TO_PHONE_NUMBER_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_API_KEY_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_CLOUD_API_VERSION_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_BIZ_OPAQUE_CALLBACK_DATA_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + AGENTS_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_NAME_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_METADATA_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] + RINGING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + whatsapp_phone_number_id: str + whatsapp_to_phone_number: str + whatsapp_api_key: str + whatsapp_cloud_api_version: str + whatsapp_biz_opaque_callback_data: str + room_name: str + agents: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + participant_identity: str + participant_name: str + participant_metadata: str + participant_attributes: _containers.ScalarMap[str, str] + destination_country: str + ringing_timeout: _duration_pb2.Duration + def __init__(self, whatsapp_phone_number_id: _Optional[str] = ..., whatsapp_to_phone_number: _Optional[str] = ..., whatsapp_api_key: _Optional[str] = ..., whatsapp_cloud_api_version: _Optional[str] = ..., whatsapp_biz_opaque_callback_data: _Optional[str] = ..., room_name: _Optional[str] = ..., agents: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., destination_country: _Optional[str] = ..., ringing_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class DialWhatsAppCallResponse(_message.Message): + __slots__ = ("whatsapp_call_id", "room_name") + WHATSAPP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + whatsapp_call_id: str + room_name: str + def __init__(self, whatsapp_call_id: _Optional[str] = ..., room_name: _Optional[str] = ...) -> None: ... + +class DisconnectWhatsAppCallRequest(_message.Message): + __slots__ = ("whatsapp_call_id", "whatsapp_api_key", "disconnect_reason") + class DisconnectReason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + BUSINESS_INITIATED: _ClassVar[DisconnectWhatsAppCallRequest.DisconnectReason] + USER_INITIATED: _ClassVar[DisconnectWhatsAppCallRequest.DisconnectReason] + BUSINESS_INITIATED: DisconnectWhatsAppCallRequest.DisconnectReason + USER_INITIATED: DisconnectWhatsAppCallRequest.DisconnectReason + WHATSAPP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_API_KEY_FIELD_NUMBER: _ClassVar[int] + DISCONNECT_REASON_FIELD_NUMBER: _ClassVar[int] + whatsapp_call_id: str + whatsapp_api_key: str + disconnect_reason: DisconnectWhatsAppCallRequest.DisconnectReason + def __init__(self, whatsapp_call_id: _Optional[str] = ..., whatsapp_api_key: _Optional[str] = ..., disconnect_reason: _Optional[_Union[DisconnectWhatsAppCallRequest.DisconnectReason, str]] = ...) -> None: ... + +class DisconnectWhatsAppCallResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class ConnectWhatsAppCallRequest(_message.Message): + __slots__ = ("whatsapp_call_id", "sdp", "wait_until_answered") + WHATSAPP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + SDP_FIELD_NUMBER: _ClassVar[int] + WAIT_UNTIL_ANSWERED_FIELD_NUMBER: _ClassVar[int] + whatsapp_call_id: str + sdp: _rtc.SessionDescription + wait_until_answered: bool + def __init__(self, whatsapp_call_id: _Optional[str] = ..., sdp: _Optional[_Union[_rtc.SessionDescription, _Mapping]] = ..., wait_until_answered: bool = ...) -> None: ... + +class ConnectWhatsAppCallResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class AcceptWhatsAppCallRequest(_message.Message): + __slots__ = ("whatsapp_phone_number_id", "whatsapp_api_key", "whatsapp_cloud_api_version", "whatsapp_call_id", "whatsapp_biz_opaque_callback_data", "sdp", "room_name", "agents", "participant_identity", "participant_name", "participant_metadata", "participant_attributes", "destination_country", "ringing_timeout", "wait_until_answered") + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + WHATSAPP_PHONE_NUMBER_ID_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_API_KEY_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_CLOUD_API_VERSION_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + WHATSAPP_BIZ_OPAQUE_CALLBACK_DATA_FIELD_NUMBER: _ClassVar[int] + SDP_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + AGENTS_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_NAME_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_METADATA_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] + RINGING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + WAIT_UNTIL_ANSWERED_FIELD_NUMBER: _ClassVar[int] + whatsapp_phone_number_id: str + whatsapp_api_key: str + whatsapp_cloud_api_version: str + whatsapp_call_id: str + whatsapp_biz_opaque_callback_data: str + sdp: _rtc.SessionDescription + room_name: str + agents: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + participant_identity: str + participant_name: str + participant_metadata: str + participant_attributes: _containers.ScalarMap[str, str] + destination_country: str + ringing_timeout: _duration_pb2.Duration + wait_until_answered: bool + def __init__(self, whatsapp_phone_number_id: _Optional[str] = ..., whatsapp_api_key: _Optional[str] = ..., whatsapp_cloud_api_version: _Optional[str] = ..., whatsapp_call_id: _Optional[str] = ..., whatsapp_biz_opaque_callback_data: _Optional[str] = ..., sdp: _Optional[_Union[_rtc.SessionDescription, _Mapping]] = ..., room_name: _Optional[str] = ..., agents: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., destination_country: _Optional[str] = ..., ringing_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., wait_until_answered: bool = ...) -> None: ... + +class AcceptWhatsAppCallResponse(_message.Message): + __slots__ = ("room_name",) + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + room_name: str + def __init__(self, room_name: _Optional[str] = ...) -> None: ... + +class WhatsAppCall(_message.Message): + __slots__ = ("whatsapp_call_id", "direction") + WHATSAPP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + DIRECTION_FIELD_NUMBER: _ClassVar[int] + whatsapp_call_id: str + direction: WhatsAppCallDirection + def __init__(self, whatsapp_call_id: _Optional[str] = ..., direction: _Optional[_Union[WhatsAppCallDirection, str]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/egress.py b/livekit-protocol/livekit/protocol/egress.py index 1fc90211..510e1fad 100644 --- a/livekit-protocol/livekit/protocol/egress.py +++ b/livekit-protocol/livekit/protocol/egress.py @@ -1,132 +1,235 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_egress.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_egress.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from . import models as _models_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14livekit_egress.proto\x12\x07livekit\x1a\x14livekit_models.proto\"\xcd\x04\n\x1aRoomCompositeEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x0e\n\x06layout\x18\x02 \x01(\t\x12\x12\n\naudio_only\x18\x03 \x01(\x08\x12\x12\n\nvideo_only\x18\x04 \x01(\x08\x12\x17\n\x0f\x63ustom_base_url\x18\x05 \x01(\t\x12.\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x07 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\n \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x08 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\t \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\x0b \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x0c \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\r \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\x0e \x03(\x0b\x32\x14.livekit.ImageOutputB\x08\n\x06outputB\t\n\x07options\"\xb0\x04\n\x10WebEgressRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\naudio_only\x18\x02 \x01(\x08\x12\x12\n\nvideo_only\x18\x03 \x01(\x08\x12\x1a\n\x12\x61wait_start_signal\x18\x0c \x01(\x08\x12.\n\x04\x66ile\x18\x04 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x05 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\x06 \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x07 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x08 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\t \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\n \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\x0b \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\r \x03(\x0b\x32\x14.livekit.ImageOutputB\x08\n\x06outputB\t\n\x07options\"\x85\x03\n\x18ParticipantEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x14\n\x0cscreen_share\x18\x03 \x01(\x08\x12\x30\n\x06preset\x18\x04 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x00\x12,\n\x08\x61\x64vanced\x18\x05 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x00\x12\x30\n\x0c\x66ile_outputs\x18\x06 \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x07 \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\x08 \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\t \x03(\x0b\x32\x14.livekit.ImageOutputB\t\n\x07options\"\xad\x04\n\x1bTrackCompositeEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x16\n\x0e\x61udio_track_id\x18\x02 \x01(\t\x12\x16\n\x0evideo_track_id\x18\x03 \x01(\t\x12.\n\x04\x66ile\x18\x04 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x05 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\x08 \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x06 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x07 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\x0b \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x0c \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\r \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\x0e \x03(\x0b\x32\x14.livekit.ImageOutputB\x08\n\x06outputB\t\n\x07options\"\x87\x01\n\x12TrackEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x10\n\x08track_id\x18\x02 \x01(\t\x12)\n\x04\x66ile\x18\x03 \x01(\x0b\x32\x19.livekit.DirectFileOutputH\x00\x12\x17\n\rwebsocket_url\x18\x04 \x01(\tH\x00\x42\x08\n\x06output\"\x8e\x02\n\x11\x45ncodedFileOutput\x12+\n\tfile_type\x18\x01 \x01(\x0e\x32\x18.livekit.EncodedFileType\x12\x10\n\x08\x66ilepath\x18\x02 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x06 \x01(\x08\x12\x1f\n\x02s3\x18\x03 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x04 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x05 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x07 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xa0\x03\n\x13SegmentedFileOutput\x12\x30\n\x08protocol\x18\x01 \x01(\x0e\x32\x1e.livekit.SegmentedFileProtocol\x12\x17\n\x0f\x66ilename_prefix\x18\x02 \x01(\t\x12\x15\n\rplaylist_name\x18\x03 \x01(\t\x12\x1a\n\x12live_playlist_name\x18\x0b \x01(\t\x12\x18\n\x10segment_duration\x18\x04 \x01(\r\x12\x35\n\x0f\x66ilename_suffix\x18\n \x01(\x0e\x32\x1c.livekit.SegmentedFileSuffix\x12\x18\n\x10\x64isable_manifest\x18\x08 \x01(\x08\x12\x1f\n\x02s3\x18\x05 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x06 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x07 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\t \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xe0\x01\n\x10\x44irectFileOutput\x12\x10\n\x08\x66ilepath\x18\x01 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x05 \x01(\x08\x12\x1f\n\x02s3\x18\x02 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x03 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x04 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x06 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xf8\x02\n\x0bImageOutput\x12\x18\n\x10\x63\x61pture_interval\x18\x01 \x01(\r\x12\r\n\x05width\x18\x02 \x01(\x05\x12\x0e\n\x06height\x18\x03 \x01(\x05\x12\x17\n\x0f\x66ilename_prefix\x18\x04 \x01(\t\x12\x31\n\x0f\x66ilename_suffix\x18\x05 \x01(\x0e\x32\x18.livekit.ImageFileSuffix\x12(\n\x0bimage_codec\x18\x06 \x01(\x0e\x32\x13.livekit.ImageCodec\x12\x18\n\x10\x64isable_manifest\x18\x07 \x01(\x08\x12\x1f\n\x02s3\x18\x08 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\t \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\n \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x0b \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xc8\x02\n\x08S3Upload\x12\x12\n\naccess_key\x18\x01 \x01(\t\x12\x0e\n\x06secret\x18\x02 \x01(\t\x12\x15\n\rsession_token\x18\x0b \x01(\t\x12\x0e\n\x06region\x18\x03 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x04 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x05 \x01(\t\x12\x18\n\x10\x66orce_path_style\x18\x06 \x01(\x08\x12\x31\n\x08metadata\x18\x07 \x03(\x0b\x32\x1f.livekit.S3Upload.MetadataEntry\x12\x0f\n\x07tagging\x18\x08 \x01(\t\x12\x1b\n\x13\x63ontent_disposition\x18\t \x01(\t\x12#\n\x05proxy\x18\n \x01(\x0b\x32\x14.livekit.ProxyConfig\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"U\n\tGCPUpload\x12\x13\n\x0b\x63redentials\x18\x01 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12#\n\x05proxy\x18\x03 \x01(\x0b\x32\x14.livekit.ProxyConfig\"T\n\x0f\x41zureBlobUpload\x12\x14\n\x0c\x61\x63\x63ount_name\x18\x01 \x01(\t\x12\x13\n\x0b\x61\x63\x63ount_key\x18\x02 \x01(\t\x12\x16\n\x0e\x63ontainer_name\x18\x03 \x01(\t\"d\n\x0c\x41liOSSUpload\x12\x12\n\naccess_key\x18\x01 \x01(\t\x12\x0e\n\x06secret\x18\x02 \x01(\t\x12\x0e\n\x06region\x18\x03 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x04 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x05 \x01(\t\">\n\x0bProxyConfig\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\"G\n\x0cStreamOutput\x12)\n\x08protocol\x18\x01 \x01(\x0e\x32\x17.livekit.StreamProtocol\x12\x0c\n\x04urls\x18\x02 \x03(\t\"\xb7\x02\n\x0f\x45ncodingOptions\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\r\n\x05\x64\x65pth\x18\x03 \x01(\x05\x12\x11\n\tframerate\x18\x04 \x01(\x05\x12(\n\x0b\x61udio_codec\x18\x05 \x01(\x0e\x32\x13.livekit.AudioCodec\x12\x15\n\raudio_bitrate\x18\x06 \x01(\x05\x12\x15\n\raudio_quality\x18\x0b \x01(\x05\x12\x17\n\x0f\x61udio_frequency\x18\x07 \x01(\x05\x12(\n\x0bvideo_codec\x18\x08 \x01(\x0e\x32\x13.livekit.VideoCodec\x12\x15\n\rvideo_bitrate\x18\t \x01(\x05\x12\x15\n\rvideo_quality\x18\x0c \x01(\x05\x12\x1a\n\x12key_frame_interval\x18\n \x01(\x01\"8\n\x13UpdateLayoutRequest\x12\x11\n\tegress_id\x18\x01 \x01(\t\x12\x0e\n\x06layout\x18\x02 \x01(\t\"]\n\x13UpdateStreamRequest\x12\x11\n\tegress_id\x18\x01 \x01(\t\x12\x17\n\x0f\x61\x64\x64_output_urls\x18\x02 \x03(\t\x12\x1a\n\x12remove_output_urls\x18\x03 \x03(\t\"I\n\x11ListEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x11\n\tegress_id\x18\x02 \x01(\t\x12\x0e\n\x06\x61\x63tive\x18\x03 \x01(\x08\"8\n\x12ListEgressResponse\x12\"\n\x05items\x18\x01 \x03(\x0b\x32\x13.livekit.EgressInfo\"&\n\x11StopEgressRequest\x12\x11\n\tegress_id\x18\x01 \x01(\t\"\xb6\x06\n\nEgressInfo\x12\x11\n\tegress_id\x18\x01 \x01(\t\x12\x0f\n\x07room_id\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\r \x01(\t\x12%\n\x06status\x18\x03 \x01(\x0e\x32\x15.livekit.EgressStatus\x12\x12\n\nstarted_at\x18\n \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x0b \x01(\x03\x12\x12\n\nupdated_at\x18\x12 \x01(\x03\x12\x0f\n\x07\x64\x65tails\x18\x15 \x01(\t\x12\r\n\x05\x65rror\x18\t \x01(\t\x12\x12\n\nerror_code\x18\x16 \x01(\x05\x12=\n\x0eroom_composite\x18\x04 \x01(\x0b\x32#.livekit.RoomCompositeEgressRequestH\x00\x12(\n\x03web\x18\x0e \x01(\x0b\x32\x19.livekit.WebEgressRequestH\x00\x12\x38\n\x0bparticipant\x18\x13 \x01(\x0b\x32!.livekit.ParticipantEgressRequestH\x00\x12?\n\x0ftrack_composite\x18\x05 \x01(\x0b\x32$.livekit.TrackCompositeEgressRequestH\x00\x12,\n\x05track\x18\x06 \x01(\x0b\x32\x1b.livekit.TrackEgressRequestH\x00\x12-\n\x06stream\x18\x07 \x01(\x0b\x32\x17.livekit.StreamInfoListB\x02\x18\x01H\x01\x12%\n\x04\x66ile\x18\x08 \x01(\x0b\x32\x11.livekit.FileInfoB\x02\x18\x01H\x01\x12-\n\x08segments\x18\x0c \x01(\x0b\x32\x15.livekit.SegmentsInfoB\x02\x18\x01H\x01\x12+\n\x0estream_results\x18\x0f \x03(\x0b\x32\x13.livekit.StreamInfo\x12\'\n\x0c\x66ile_results\x18\x10 \x03(\x0b\x32\x11.livekit.FileInfo\x12.\n\x0fsegment_results\x18\x11 \x03(\x0b\x32\x15.livekit.SegmentsInfo\x12*\n\rimage_results\x18\x14 \x03(\x0b\x32\x13.livekit.ImagesInfoB\t\n\x07requestB\x08\n\x06result\"7\n\x0eStreamInfoList\x12!\n\x04info\x18\x01 \x03(\x0b\x32\x13.livekit.StreamInfo:\x02\x18\x01\"\xbc\x01\n\nStreamInfo\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\x12\x10\n\x08\x64uration\x18\x04 \x01(\x03\x12*\n\x06status\x18\x05 \x01(\x0e\x32\x1a.livekit.StreamInfo.Status\x12\r\n\x05\x65rror\x18\x06 \x01(\t\".\n\x06Status\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\x0c\n\x08\x46INISHED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"t\n\x08\x46ileInfo\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\x12\x10\n\x08\x64uration\x18\x06 \x01(\x03\x12\x0c\n\x04size\x18\x04 \x01(\x03\x12\x10\n\x08location\x18\x05 \x01(\t\"\xd9\x01\n\x0cSegmentsInfo\x12\x15\n\rplaylist_name\x18\x01 \x01(\t\x12\x1a\n\x12live_playlist_name\x18\x08 \x01(\t\x12\x10\n\x08\x64uration\x18\x02 \x01(\x03\x12\x0c\n\x04size\x18\x03 \x01(\x03\x12\x19\n\x11playlist_location\x18\x04 \x01(\t\x12\x1e\n\x16live_playlist_location\x18\t \x01(\t\x12\x15\n\rsegment_count\x18\x05 \x01(\x03\x12\x12\n\nstarted_at\x18\x06 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x07 \x01(\x03\"G\n\nImagesInfo\x12\x13\n\x0bimage_count\x18\x01 \x01(\x03\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\"\xeb\x01\n\x15\x41utoParticipantEgress\x12\x30\n\x06preset\x18\x01 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x00\x12,\n\x08\x61\x64vanced\x18\x02 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x00\x12\x30\n\x0c\x66ile_outputs\x18\x03 \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12\x35\n\x0fsegment_outputs\x18\x04 \x03(\x0b\x32\x1c.livekit.SegmentedFileOutputB\t\n\x07options\"\xdf\x01\n\x0f\x41utoTrackEgress\x12\x10\n\x08\x66ilepath\x18\x01 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x05 \x01(\x08\x12\x1f\n\x02s3\x18\x02 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x03 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x04 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x06 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output*9\n\x0f\x45ncodedFileType\x12\x14\n\x10\x44\x45\x46\x41ULT_FILETYPE\x10\x00\x12\x07\n\x03MP4\x10\x01\x12\x07\n\x03OGG\x10\x02*N\n\x15SegmentedFileProtocol\x12#\n\x1f\x44\x45\x46\x41ULT_SEGMENTED_FILE_PROTOCOL\x10\x00\x12\x10\n\x0cHLS_PROTOCOL\x10\x01*/\n\x13SegmentedFileSuffix\x12\t\n\x05INDEX\x10\x00\x12\r\n\tTIMESTAMP\x10\x01*E\n\x0fImageFileSuffix\x12\x16\n\x12IMAGE_SUFFIX_INDEX\x10\x00\x12\x1a\n\x16IMAGE_SUFFIX_TIMESTAMP\x10\x01*0\n\x0eStreamProtocol\x12\x14\n\x10\x44\x45\x46\x41ULT_PROTOCOL\x10\x00\x12\x08\n\x04RTMP\x10\x01*\xcf\x01\n\x15\x45ncodingOptionsPreset\x12\x10\n\x0cH264_720P_30\x10\x00\x12\x10\n\x0cH264_720P_60\x10\x01\x12\x11\n\rH264_1080P_30\x10\x02\x12\x11\n\rH264_1080P_60\x10\x03\x12\x19\n\x15PORTRAIT_H264_720P_30\x10\x04\x12\x19\n\x15PORTRAIT_H264_720P_60\x10\x05\x12\x1a\n\x16PORTRAIT_H264_1080P_30\x10\x06\x12\x1a\n\x16PORTRAIT_H264_1080P_60\x10\x07*\x9f\x01\n\x0c\x45gressStatus\x12\x13\n\x0f\x45GRESS_STARTING\x10\x00\x12\x11\n\rEGRESS_ACTIVE\x10\x01\x12\x11\n\rEGRESS_ENDING\x10\x02\x12\x13\n\x0f\x45GRESS_COMPLETE\x10\x03\x12\x11\n\rEGRESS_FAILED\x10\x04\x12\x12\n\x0e\x45GRESS_ABORTED\x10\x05\x12\x18\n\x14\x45GRESS_LIMIT_REACHED\x10\x06\x32\x9c\x05\n\x06\x45gress\x12T\n\x18StartRoomCompositeEgress\x12#.livekit.RoomCompositeEgressRequest\x1a\x13.livekit.EgressInfo\x12@\n\x0eStartWebEgress\x12\x19.livekit.WebEgressRequest\x1a\x13.livekit.EgressInfo\x12P\n\x16StartParticipantEgress\x12!.livekit.ParticipantEgressRequest\x1a\x13.livekit.EgressInfo\x12V\n\x19StartTrackCompositeEgress\x12$.livekit.TrackCompositeEgressRequest\x1a\x13.livekit.EgressInfo\x12\x44\n\x10StartTrackEgress\x12\x1b.livekit.TrackEgressRequest\x1a\x13.livekit.EgressInfo\x12\x41\n\x0cUpdateLayout\x12\x1c.livekit.UpdateLayoutRequest\x1a\x13.livekit.EgressInfo\x12\x41\n\x0cUpdateStream\x12\x1c.livekit.UpdateStreamRequest\x1a\x13.livekit.EgressInfo\x12\x45\n\nListEgress\x12\x1a.livekit.ListEgressRequest\x1a\x1b.livekit.ListEgressResponse\x12=\n\nStopEgress\x12\x1a.livekit.StopEgressRequest\x1a\x13.livekit.EgressInfoBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14livekit_egress.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14logger/options.proto\"\x89\x03\n\x12StartEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12+\n\x08template\x18\x02 \x01(\x0b\x32\x17.livekit.TemplateSourceH\x00\x12!\n\x03web\x18\x03 \x01(\x0b\x32\x12.livekit.WebSourceH\x00\x12%\n\x05media\x18\x04 \x01(\x0b\x32\x14.livekit.MediaSourceH\x00\x12\x30\n\x06preset\x18\x05 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x06 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12 \n\x07outputs\x18\x07 \x03(\x0b\x32\x0f.livekit.Output\x12\'\n\x07storage\x18\x08 \x01(\x0b\x32\x16.livekit.StorageConfig\x12(\n\x08webhooks\x18\t \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06sourceB\n\n\x08\x65ncoding\"a\n\x0eTemplateSource\x12\x0e\n\x06layout\x18\x01 \x01(\t\x12\x12\n\naudio_only\x18\x02 \x01(\x08\x12\x12\n\nvideo_only\x18\x03 \x01(\x08\x12\x17\n\x0f\x63ustom_base_url\x18\x04 \x01(\t\"\\\n\tWebSource\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\naudio_only\x18\x02 \x01(\x08\x12\x12\n\nvideo_only\x18\x03 \x01(\x08\x12\x1a\n\x12\x61wait_start_signal\x18\x04 \x01(\x08\"\x9e\x01\n\x0bMediaSource\x12)\n\x0evideo_track_id\x18\x01 \x01(\tB\x0f\xbaP\x0cvideoTrackIDH\x00\x12\x36\n\x11participant_video\x18\x02 \x01(\x0b\x32\x19.livekit.ParticipantVideoH\x00\x12#\n\x05\x61udio\x18\x03 \x01(\x0b\x32\x14.livekit.AudioConfigB\x07\n\x05video\"A\n\x10ParticipantVideo\x12\x10\n\x08identity\x18\x01 \x01(\t\x12\x1b\n\x13prefer_screen_share\x18\x02 \x01(\x08\"G\n\x0b\x41udioConfig\x12\x13\n\x0b\x63\x61pture_all\x18\x01 \x01(\x08\x12#\n\x06routes\x18\x02 \x03(\x0b\x32\x13.livekit.AudioRoute\"\xb8\x01\n\nAudioRoute\x12\x1e\n\x08track_id\x18\x01 \x01(\tB\n\xbaP\x07trackIDH\x00\x12\x1e\n\x14participant_identity\x18\x02 \x01(\tH\x00\x12\x39\n\x10participant_kind\x18\x03 \x01(\x0e\x32\x1d.livekit.ParticipantInfo.KindH\x00\x12&\n\x07\x63hannel\x18\x04 \x01(\x0e\x32\x15.livekit.AudioChannelB\x07\n\x05match\"K\n\nDataConfig\x12\x13\n\x0b\x63\x61pture_all\x18\x01 \x01(\x08\x12(\n\tselectors\x18\x02 \x03(\x0b\x32\x15.livekit.DataSelector\"W\n\x0c\x44\x61taSelector\x12\x1e\n\x08track_id\x18\x01 \x01(\tB\n\xbaP\x07trackIDH\x00\x12\x1e\n\x14participant_identity\x18\x02 \x01(\tH\x00\x42\x07\n\x05match\"\xbf\x02\n\x0f\x45ncodingOptions\x12\r\n\x05width\x18\x01 \x01(\x05\x12\x0e\n\x06height\x18\x02 \x01(\x05\x12\r\n\x05\x64\x65pth\x18\x03 \x01(\x05\x12\x11\n\tframerate\x18\x04 \x01(\x05\x12(\n\x0b\x61udio_codec\x18\x05 \x01(\x0e\x32\x13.livekit.AudioCodec\x12\x15\n\raudio_bitrate\x18\x06 \x01(\x05\x12\x17\n\x0f\x61udio_frequency\x18\x07 \x01(\x05\x12(\n\x0bvideo_codec\x18\x08 \x01(\x0e\x32\x13.livekit.VideoCodec\x12\x15\n\rvideo_bitrate\x18\t \x01(\x05\x12\x1a\n\x12key_frame_interval\x18\n \x01(\x01\x12\x19\n\raudio_quality\x18\x0b \x01(\x05\x42\x02\x18\x01\x12\x19\n\rvideo_quality\x18\x0c \x01(\x05\x42\x02\x18\x01\"\xe3\x01\n\x06Output\x12#\n\x04\x66ile\x18\x01 \x01(\x0b\x32\x13.livekit.FileOutputH\x00\x12\'\n\x06stream\x18\x02 \x01(\x0b\x32\x15.livekit.StreamOutputH\x00\x12\x30\n\x08segments\x18\x03 \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputH\x00\x12&\n\x06images\x18\x04 \x01(\x0b\x32\x14.livekit.ImageOutputH\x00\x12\'\n\x07storage\x18\x06 \x01(\x0b\x32\x16.livekit.StorageConfigB\x08\n\x06\x63onfig\"e\n\nFileOutput\x12+\n\tfile_type\x18\x01 \x01(\x0e\x32\x18.livekit.EncodedFileType\x12\x10\n\x08\x66ilepath\x18\x02 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x03 \x01(\x08\"G\n\x0cStreamOutput\x12)\n\x08protocol\x18\x01 \x01(\x0e\x32\x17.livekit.StreamProtocol\x12\x0c\n\x04urls\x18\x02 \x03(\t\"\xa0\x03\n\x13SegmentedFileOutput\x12\x30\n\x08protocol\x18\x01 \x01(\x0e\x32\x1e.livekit.SegmentedFileProtocol\x12\x17\n\x0f\x66ilename_prefix\x18\x02 \x01(\t\x12\x15\n\rplaylist_name\x18\x03 \x01(\t\x12\x1a\n\x12live_playlist_name\x18\x0b \x01(\t\x12\x18\n\x10segment_duration\x18\x04 \x01(\r\x12\x35\n\x0f\x66ilename_suffix\x18\n \x01(\x0e\x32\x1c.livekit.SegmentedFileSuffix\x12\x18\n\x10\x64isable_manifest\x18\x08 \x01(\x08\x12\x1f\n\x02s3\x18\x05 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x06 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x07 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\t \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xf8\x02\n\x0bImageOutput\x12\x18\n\x10\x63\x61pture_interval\x18\x01 \x01(\r\x12\r\n\x05width\x18\x02 \x01(\x05\x12\x0e\n\x06height\x18\x03 \x01(\x05\x12\x17\n\x0f\x66ilename_prefix\x18\x04 \x01(\t\x12\x31\n\x0f\x66ilename_suffix\x18\x05 \x01(\x0e\x32\x18.livekit.ImageFileSuffix\x12(\n\x0bimage_codec\x18\x06 \x01(\x0e\x32\x13.livekit.ImageCodec\x12\x18\n\x10\x64isable_manifest\x18\x07 \x01(\x08\x12\x1f\n\x02s3\x18\x08 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\t \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\n \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x0b \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xb3\x01\n\rStorageConfig\x12\x1f\n\x02s3\x18\x01 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x02 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x03 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x04 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\n\n\x08provider\"\xc1\x03\n\x08S3Upload\x12\x17\n\naccess_key\x18\x01 \x01(\tB\x03\xc0P\x02\x12\x13\n\x06secret\x18\x02 \x01(\tB\x03\xc0P\x02\x12\x1a\n\rsession_token\x18\x0b \x01(\tB\x03\xc0P\x02\x12\x1c\n\x0f\x61ssume_role_arn\x18\x0c \x01(\tB\x03\xc0P\x01\x12;\n\x17\x61ssume_role_external_id\x18\r \x01(\tB\x1a\xbaP\x14\x61ssumeRoleExternalID\xc0P\x02\x12\x0e\n\x06region\x18\x03 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x04 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x05 \x01(\t\x12\x18\n\x10\x66orce_path_style\x18\x06 \x01(\x08\x12\x36\n\x08metadata\x18\x07 \x03(\x0b\x32\x1f.livekit.S3Upload.MetadataEntryB\x03\xc0P\x01\x12\x14\n\x07tagging\x18\x08 \x01(\tB\x03\xc0P\x01\x12 \n\x13\x63ontent_disposition\x18\t \x01(\tB\x03\xc0P\x01\x12#\n\x05proxy\x18\n \x01(\x0b\x32\x14.livekit.ProxyConfig\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"Z\n\tGCPUpload\x12\x18\n\x0b\x63redentials\x18\x01 \x01(\tB\x03\xc0P\x02\x12\x0e\n\x06\x62ucket\x18\x02 \x01(\t\x12#\n\x05proxy\x18\x03 \x01(\x0b\x32\x14.livekit.ProxyConfig\"^\n\x0f\x41zureBlobUpload\x12\x19\n\x0c\x61\x63\x63ount_name\x18\x01 \x01(\tB\x03\xc0P\x01\x12\x18\n\x0b\x61\x63\x63ount_key\x18\x02 \x01(\tB\x03\xc0P\x02\x12\x16\n\x0e\x63ontainer_name\x18\x03 \x01(\t\"n\n\x0c\x41liOSSUpload\x12\x17\n\naccess_key\x18\x01 \x01(\tB\x03\xc0P\x02\x12\x13\n\x06secret\x18\x02 \x01(\tB\x03\xc0P\x02\x12\x0e\n\x06region\x18\x03 \x01(\t\x12\x10\n\x08\x65ndpoint\x18\x04 \x01(\t\x12\x0e\n\x06\x62ucket\x18\x05 \x01(\t\"C\n\x0bProxyConfig\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x15\n\x08password\x18\x03 \x01(\tB\x03\xc0P\x02\"\x84\x01\n\x11ListEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x1e\n\tegress_id\x18\x02 \x01(\tB\x0b\xbaP\x08\x65gressID\x12\x0e\n\x06\x61\x63tive\x18\x03 \x01(\x08\x12,\n\npage_token\x18\x04 \x01(\x0b\x32\x18.livekit.TokenPagination\"k\n\x12ListEgressResponse\x12\"\n\x05items\x18\x01 \x03(\x0b\x32\x13.livekit.EgressInfo\x12\x31\n\x0fnext_page_token\x18\x02 \x01(\x0b\x32\x18.livekit.TokenPagination\"\x87\x01\n\x13UpdateEgressRequest\x12\x1e\n\tegress_id\x18\x01 \x01(\tB\x0b\xbaP\x08\x65gressID\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0e\n\x06layout\x18\x03 \x01(\t\x12\x17\n\x0f\x61\x64\x64_stream_urls\x18\x04 \x03(\t\x12\x1a\n\x12remove_stream_urls\x18\x05 \x03(\t\"3\n\x11StopEgressRequest\x12\x1e\n\tegress_id\x18\x01 \x01(\tB\x0b\xbaP\x08\x65gressID\"\xaa\x08\n\nEgressInfo\x12\x1e\n\tegress_id\x18\x01 \x01(\tB\x0b\xbaP\x08\x65gressID\x12\x1a\n\x07room_id\x18\x02 \x01(\tB\t\xbaP\x06roomID\x12\x11\n\troom_name\x18\r \x01(\t\x12.\n\x0bsource_type\x18\x1a \x01(\x0e\x32\x19.livekit.EgressSourceType\x12%\n\x06status\x18\x03 \x01(\x0e\x32\x15.livekit.EgressStatus\x12\x12\n\nstarted_at\x18\n \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x0b \x01(\x03\x12\x12\n\nupdated_at\x18\x12 \x01(\x03\x12-\n\x06\x65gress\x18\x1d \x01(\x0b\x32\x1b.livekit.StartEgressRequestH\x00\x12.\n\x06replay\x18\x1e \x01(\x0b\x32\x1c.livekit.ExportReplayRequestH\x00\x12=\n\x0eroom_composite\x18\x04 \x01(\x0b\x32#.livekit.RoomCompositeEgressRequestH\x00\x12(\n\x03web\x18\x0e \x01(\x0b\x32\x19.livekit.WebEgressRequestH\x00\x12\x38\n\x0bparticipant\x18\x13 \x01(\x0b\x32!.livekit.ParticipantEgressRequestH\x00\x12?\n\x0ftrack_composite\x18\x05 \x01(\x0b\x32$.livekit.TrackCompositeEgressRequestH\x00\x12,\n\x05track\x18\x06 \x01(\x0b\x32\x1b.livekit.TrackEgressRequestH\x00\x12+\n\x0estream_results\x18\x0f \x03(\x0b\x32\x13.livekit.StreamInfo\x12\'\n\x0c\x66ile_results\x18\x10 \x03(\x0b\x32\x11.livekit.FileInfo\x12.\n\x0fsegment_results\x18\x11 \x03(\x0b\x32\x15.livekit.SegmentsInfo\x12*\n\rimage_results\x18\x14 \x03(\x0b\x32\x13.livekit.ImagesInfo\x12\r\n\x05\x65rror\x18\t \x01(\t\x12\x12\n\nerror_code\x18\x16 \x01(\x05\x12\x0f\n\x07\x64\x65tails\x18\x15 \x01(\t\x12\x19\n\x11manifest_location\x18\x17 \x01(\t\x12\x1b\n\x13\x62\x61\x63kup_storage_used\x18\x19 \x01(\x08\x12\x13\n\x0bretry_count\x18\x1b \x01(\x05\x12-\n\x06stream\x18\x07 \x01(\x0b\x32\x17.livekit.StreamInfoListB\x02\x18\x01H\x01\x12%\n\x04\x66ile\x18\x08 \x01(\x0b\x32\x11.livekit.FileInfoB\x02\x18\x01H\x01\x12-\n\x08segments\x18\x0c \x01(\x0b\x32\x15.livekit.SegmentsInfoB\x02\x18\x01H\x01\x42\t\n\x07requestB\x08\n\x06result\"\xe4\x01\n\nStreamInfo\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\x12\x10\n\x08\x64uration\x18\x04 \x01(\x03\x12*\n\x06status\x18\x05 \x01(\x0e\x32\x1a.livekit.StreamInfo.Status\x12\r\n\x05\x65rror\x18\x06 \x01(\t\x12\x15\n\rlast_retry_at\x18\x07 \x01(\x03\x12\x0f\n\x07retries\x18\x08 \x01(\r\".\n\x06Status\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\x0c\n\x08\x46INISHED\x10\x01\x12\n\n\x06\x46\x41ILED\x10\x02\"t\n\x08\x46ileInfo\x12\x10\n\x08\x66ilename\x18\x01 \x01(\t\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\x12\x10\n\x08\x64uration\x18\x06 \x01(\x03\x12\x0c\n\x04size\x18\x04 \x01(\x03\x12\x10\n\x08location\x18\x05 \x01(\t\"\xd9\x01\n\x0cSegmentsInfo\x12\x15\n\rplaylist_name\x18\x01 \x01(\t\x12\x1a\n\x12live_playlist_name\x18\x08 \x01(\t\x12\x10\n\x08\x64uration\x18\x02 \x01(\x03\x12\x0c\n\x04size\x18\x03 \x01(\x03\x12\x19\n\x11playlist_location\x18\x04 \x01(\t\x12\x1e\n\x16live_playlist_location\x18\t \x01(\t\x12\x15\n\rsegment_count\x18\x05 \x01(\x03\x12\x12\n\nstarted_at\x18\x06 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x07 \x01(\x03\"`\n\nImagesInfo\x12\x17\n\x0f\x66ilename_prefix\x18\x04 \x01(\t\x12\x13\n\x0bimage_count\x18\x01 \x01(\x03\x12\x12\n\nstarted_at\x18\x02 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x03 \x01(\x03\"\xeb\x01\n\x15\x41utoParticipantEgress\x12\x30\n\x06preset\x18\x01 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x00\x12,\n\x08\x61\x64vanced\x18\x02 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x00\x12\x30\n\x0c\x66ile_outputs\x18\x03 \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12\x35\n\x0fsegment_outputs\x18\x04 \x03(\x0b\x32\x1c.livekit.SegmentedFileOutputB\t\n\x07options\"\xdf\x01\n\x0f\x41utoTrackEgress\x12\x10\n\x08\x66ilepath\x18\x01 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x05 \x01(\x08\x12\x1f\n\x02s3\x18\x02 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x03 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x04 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x06 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\xc7\x03\n\x13\x45xportReplayRequest\x12\x1e\n\treplay_id\x18\x01 \x01(\tB\x0b\xbaP\x08replayID\x12\x17\n\x0fstart_offset_ms\x18\x02 \x01(\x03\x12\x15\n\rend_offset_ms\x18\x03 \x01(\x03\x12+\n\x08template\x18\x04 \x01(\x0b\x32\x17.livekit.TemplateSourceH\x00\x12!\n\x03web\x18\x05 \x01(\x0b\x32\x12.livekit.WebSourceH\x00\x12%\n\x05media\x18\x06 \x01(\x0b\x32\x14.livekit.MediaSourceH\x00\x12\x30\n\x06preset\x18\x07 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x08 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12 \n\x07outputs\x18\t \x03(\x0b\x32\x0f.livekit.Output\x12\'\n\x07storage\x18\n \x01(\x0b\x32\x16.livekit.StorageConfig\x12(\n\x08webhooks\x18\x0b \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06sourceB\n\n\x08\x65ncoding\"\xa3\x05\n\x1aRoomCompositeEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x0e\n\x06layout\x18\x02 \x01(\t\x12\x12\n\naudio_only\x18\x03 \x01(\x08\x12*\n\x0c\x61udio_mixing\x18\x0f \x01(\x0e\x32\x14.livekit.AudioMixing\x12\x12\n\nvideo_only\x18\x04 \x01(\x08\x12\x17\n\x0f\x63ustom_base_url\x18\x05 \x01(\t\x12.\n\x04\x66ile\x18\x06 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x07 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\n \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x08 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\t \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\x0b \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x0c \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\r \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\x0e \x03(\x0b\x32\x14.livekit.ImageOutput\x12(\n\x08webhooks\x18\x10 \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06outputB\t\n\x07options\"\xda\x04\n\x10WebEgressRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x12\n\naudio_only\x18\x02 \x01(\x08\x12\x12\n\nvideo_only\x18\x03 \x01(\x08\x12\x1a\n\x12\x61wait_start_signal\x18\x0c \x01(\x08\x12.\n\x04\x66ile\x18\x04 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x05 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\x06 \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x07 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x08 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\t \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\n \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\x0b \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\r \x03(\x0b\x32\x14.livekit.ImageOutput\x12(\n\x08webhooks\x18\x0e \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06outputB\t\n\x07options\"\xaf\x03\n\x18ParticipantEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x14\n\x0cscreen_share\x18\x03 \x01(\x08\x12\x30\n\x06preset\x18\x04 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x00\x12,\n\x08\x61\x64vanced\x18\x05 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x00\x12\x30\n\x0c\x66ile_outputs\x18\x06 \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x07 \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\x08 \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\t \x03(\x0b\x32\x14.livekit.ImageOutput\x12(\n\x08webhooks\x18\n \x03(\x0b\x32\x16.livekit.WebhookConfigB\t\n\x07options\"\xf9\x04\n\x1bTrackCompositeEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\'\n\x0e\x61udio_track_id\x18\x02 \x01(\tB\x0f\xbaP\x0c\x61udioTrackID\x12\'\n\x0evideo_track_id\x18\x03 \x01(\tB\x0f\xbaP\x0cvideoTrackID\x12.\n\x04\x66ile\x18\x04 \x01(\x0b\x32\x1a.livekit.EncodedFileOutputB\x02\x18\x01H\x00\x12+\n\x06stream\x18\x05 \x01(\x0b\x32\x15.livekit.StreamOutputB\x02\x18\x01H\x00\x12\x34\n\x08segments\x18\x08 \x01(\x0b\x32\x1c.livekit.SegmentedFileOutputB\x02\x18\x01H\x00\x12\x30\n\x06preset\x18\x06 \x01(\x0e\x32\x1e.livekit.EncodingOptionsPresetH\x01\x12,\n\x08\x61\x64vanced\x18\x07 \x01(\x0b\x32\x18.livekit.EncodingOptionsH\x01\x12\x30\n\x0c\x66ile_outputs\x18\x0b \x03(\x0b\x32\x1a.livekit.EncodedFileOutput\x12-\n\x0estream_outputs\x18\x0c \x03(\x0b\x32\x15.livekit.StreamOutput\x12\x35\n\x0fsegment_outputs\x18\r \x03(\x0b\x32\x1c.livekit.SegmentedFileOutput\x12+\n\rimage_outputs\x18\x0e \x03(\x0b\x32\x14.livekit.ImageOutput\x12(\n\x08webhooks\x18\x0f \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06outputB\t\n\x07options\"\xbd\x01\n\x12TrackEgressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x1c\n\x08track_id\x18\x02 \x01(\tB\n\xbaP\x07trackID\x12)\n\x04\x66ile\x18\x03 \x01(\x0b\x32\x19.livekit.DirectFileOutputH\x00\x12\x17\n\rwebsocket_url\x18\x04 \x01(\tH\x00\x12(\n\x08webhooks\x18\x05 \x03(\x0b\x32\x16.livekit.WebhookConfigB\x08\n\x06output\"\xe0\x01\n\x10\x44irectFileOutput\x12\x10\n\x08\x66ilepath\x18\x01 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x05 \x01(\x08\x12\x1f\n\x02s3\x18\x02 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x03 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x04 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x06 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"\x8e\x02\n\x11\x45ncodedFileOutput\x12+\n\tfile_type\x18\x01 \x01(\x0e\x32\x18.livekit.EncodedFileType\x12\x10\n\x08\x66ilepath\x18\x02 \x01(\t\x12\x18\n\x10\x64isable_manifest\x18\x06 \x01(\x08\x12\x1f\n\x02s3\x18\x03 \x01(\x0b\x32\x11.livekit.S3UploadH\x00\x12!\n\x03gcp\x18\x04 \x01(\x0b\x32\x12.livekit.GCPUploadH\x00\x12)\n\x05\x61zure\x18\x05 \x01(\x0b\x32\x18.livekit.AzureBlobUploadH\x00\x12\'\n\x06\x61liOSS\x18\x07 \x01(\x0b\x32\x15.livekit.AliOSSUploadH\x00\x42\x08\n\x06output\"E\n\x13UpdateLayoutRequest\x12\x1e\n\tegress_id\x18\x01 \x01(\tB\x0b\xbaP\x08\x65gressID\x12\x0e\n\x06layout\x18\x02 \x01(\t\"j\n\x13UpdateStreamRequest\x12\x1e\n\tegress_id\x18\x01 \x01(\tB\x0b\xbaP\x08\x65gressID\x12\x17\n\x0f\x61\x64\x64_output_urls\x18\x02 \x03(\t\x12\x1a\n\x12remove_output_urls\x18\x03 \x03(\t\"3\n\x0eStreamInfoList\x12!\n\x04info\x18\x01 \x03(\x0b\x32\x13.livekit.StreamInfo*W\n\x0c\x41udioChannel\x12\x16\n\x12\x41UDIO_CHANNEL_BOTH\x10\x00\x12\x16\n\x12\x41UDIO_CHANNEL_LEFT\x10\x01\x12\x17\n\x13\x41UDIO_CHANNEL_RIGHT\x10\x02*\xcf\x01\n\x15\x45ncodingOptionsPreset\x12\x10\n\x0cH264_720P_30\x10\x00\x12\x10\n\x0cH264_720P_60\x10\x01\x12\x11\n\rH264_1080P_30\x10\x02\x12\x11\n\rH264_1080P_60\x10\x03\x12\x19\n\x15PORTRAIT_H264_720P_30\x10\x04\x12\x19\n\x15PORTRAIT_H264_720P_60\x10\x05\x12\x1a\n\x16PORTRAIT_H264_1080P_30\x10\x06\x12\x1a\n\x16PORTRAIT_H264_1080P_60\x10\x07*B\n\x0f\x45ncodedFileType\x12\x14\n\x10\x44\x45\x46\x41ULT_FILETYPE\x10\x00\x12\x07\n\x03MP4\x10\x01\x12\x07\n\x03OGG\x10\x02\x12\x07\n\x03MP3\x10\x03*H\n\x0eStreamProtocol\x12\x14\n\x10\x44\x45\x46\x41ULT_PROTOCOL\x10\x00\x12\x08\n\x04RTMP\x10\x01\x12\x07\n\x03SRT\x10\x02\x12\r\n\tWEBSOCKET\x10\x03*N\n\x15SegmentedFileProtocol\x12#\n\x1f\x44\x45\x46\x41ULT_SEGMENTED_FILE_PROTOCOL\x10\x00\x12\x10\n\x0cHLS_PROTOCOL\x10\x01*/\n\x13SegmentedFileSuffix\x12\t\n\x05INDEX\x10\x00\x12\r\n\tTIMESTAMP\x10\x01*f\n\x0fImageFileSuffix\x12\x16\n\x12IMAGE_SUFFIX_INDEX\x10\x00\x12\x1a\n\x16IMAGE_SUFFIX_TIMESTAMP\x10\x01\x12\x1f\n\x1bIMAGE_SUFFIX_NONE_OVERWRITE\x10\x02*J\n\x10\x45gressSourceType\x12\x1a\n\x16\x45GRESS_SOURCE_TYPE_WEB\x10\x00\x12\x1a\n\x16\x45GRESS_SOURCE_TYPE_SDK\x10\x01*\x9f\x01\n\x0c\x45gressStatus\x12\x13\n\x0f\x45GRESS_STARTING\x10\x00\x12\x11\n\rEGRESS_ACTIVE\x10\x01\x12\x11\n\rEGRESS_ENDING\x10\x02\x12\x13\n\x0f\x45GRESS_COMPLETE\x10\x03\x12\x11\n\rEGRESS_FAILED\x10\x04\x12\x12\n\x0e\x45GRESS_ABORTED\x10\x05\x12\x18\n\x14\x45GRESS_LIMIT_REACHED\x10\x06*U\n\x0b\x41udioMixing\x12\x12\n\x0e\x44\x45\x46\x41ULT_MIXING\x10\x00\x12\x16\n\x12\x44UAL_CHANNEL_AGENT\x10\x01\x12\x1a\n\x16\x44UAL_CHANNEL_ALTERNATE\x10\x02\x32\xdd\x05\n\x06\x45gress\x12?\n\x0bStartEgress\x12\x1b.livekit.StartEgressRequest\x1a\x13.livekit.EgressInfo\x12\x41\n\x0cUpdateLayout\x12\x1c.livekit.UpdateLayoutRequest\x1a\x13.livekit.EgressInfo\x12\x41\n\x0cUpdateStream\x12\x1c.livekit.UpdateStreamRequest\x1a\x13.livekit.EgressInfo\x12\x45\n\nListEgress\x12\x1a.livekit.ListEgressRequest\x1a\x1b.livekit.ListEgressResponse\x12=\n\nStopEgress\x12\x1a.livekit.StopEgressRequest\x1a\x13.livekit.EgressInfo\x12T\n\x18StartRoomCompositeEgress\x12#.livekit.RoomCompositeEgressRequest\x1a\x13.livekit.EgressInfo\x12@\n\x0eStartWebEgress\x12\x19.livekit.WebEgressRequest\x1a\x13.livekit.EgressInfo\x12P\n\x16StartParticipantEgress\x12!.livekit.ParticipantEgressRequest\x1a\x13.livekit.EgressInfo\x12V\n\x19StartTrackCompositeEgress\x12$.livekit.TrackCompositeEgressRequest\x1a\x13.livekit.EgressInfo\x12\x44\n\x10StartTrackEgress\x12\x1b.livekit.TrackEgressRequest\x1a\x13.livekit.EgressInfoBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'egress', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._options = None + _globals['_MEDIASOURCE'].fields_by_name['video_track_id']._loaded_options = None + _globals['_MEDIASOURCE'].fields_by_name['video_track_id']._serialized_options = b'\272P\014videoTrackID' + _globals['_AUDIOROUTE'].fields_by_name['track_id']._loaded_options = None + _globals['_AUDIOROUTE'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_DATASELECTOR'].fields_by_name['track_id']._loaded_options = None + _globals['_DATASELECTOR'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_ENCODINGOPTIONS'].fields_by_name['audio_quality']._loaded_options = None + _globals['_ENCODINGOPTIONS'].fields_by_name['audio_quality']._serialized_options = b'\030\001' + _globals['_ENCODINGOPTIONS'].fields_by_name['video_quality']._loaded_options = None + _globals['_ENCODINGOPTIONS'].fields_by_name['video_quality']._serialized_options = b'\030\001' + _globals['_S3UPLOAD_METADATAENTRY']._loaded_options = None + _globals['_S3UPLOAD_METADATAENTRY']._serialized_options = b'8\001' + _globals['_S3UPLOAD'].fields_by_name['access_key']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['access_key']._serialized_options = b'\300P\002' + _globals['_S3UPLOAD'].fields_by_name['secret']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['secret']._serialized_options = b'\300P\002' + _globals['_S3UPLOAD'].fields_by_name['session_token']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['session_token']._serialized_options = b'\300P\002' + _globals['_S3UPLOAD'].fields_by_name['assume_role_arn']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['assume_role_arn']._serialized_options = b'\300P\001' + _globals['_S3UPLOAD'].fields_by_name['assume_role_external_id']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['assume_role_external_id']._serialized_options = b'\272P\024assumeRoleExternalID\300P\002' + _globals['_S3UPLOAD'].fields_by_name['metadata']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['metadata']._serialized_options = b'\300P\001' + _globals['_S3UPLOAD'].fields_by_name['tagging']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['tagging']._serialized_options = b'\300P\001' + _globals['_S3UPLOAD'].fields_by_name['content_disposition']._loaded_options = None + _globals['_S3UPLOAD'].fields_by_name['content_disposition']._serialized_options = b'\300P\001' + _globals['_GCPUPLOAD'].fields_by_name['credentials']._loaded_options = None + _globals['_GCPUPLOAD'].fields_by_name['credentials']._serialized_options = b'\300P\002' + _globals['_AZUREBLOBUPLOAD'].fields_by_name['account_name']._loaded_options = None + _globals['_AZUREBLOBUPLOAD'].fields_by_name['account_name']._serialized_options = b'\300P\001' + _globals['_AZUREBLOBUPLOAD'].fields_by_name['account_key']._loaded_options = None + _globals['_AZUREBLOBUPLOAD'].fields_by_name['account_key']._serialized_options = b'\300P\002' + _globals['_ALIOSSUPLOAD'].fields_by_name['access_key']._loaded_options = None + _globals['_ALIOSSUPLOAD'].fields_by_name['access_key']._serialized_options = b'\300P\002' + _globals['_ALIOSSUPLOAD'].fields_by_name['secret']._loaded_options = None + _globals['_ALIOSSUPLOAD'].fields_by_name['secret']._serialized_options = b'\300P\002' + _globals['_PROXYCONFIG'].fields_by_name['password']._loaded_options = None + _globals['_PROXYCONFIG'].fields_by_name['password']._serialized_options = b'\300P\002' + _globals['_LISTEGRESSREQUEST'].fields_by_name['egress_id']._loaded_options = None + _globals['_LISTEGRESSREQUEST'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_UPDATEEGRESSREQUEST'].fields_by_name['egress_id']._loaded_options = None + _globals['_UPDATEEGRESSREQUEST'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_STOPEGRESSREQUEST'].fields_by_name['egress_id']._loaded_options = None + _globals['_STOPEGRESSREQUEST'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_EGRESSINFO'].fields_by_name['egress_id']._loaded_options = None + _globals['_EGRESSINFO'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_EGRESSINFO'].fields_by_name['room_id']._loaded_options = None + _globals['_EGRESSINFO'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_EGRESSINFO'].fields_by_name['stream']._loaded_options = None + _globals['_EGRESSINFO'].fields_by_name['stream']._serialized_options = b'\030\001' + _globals['_EGRESSINFO'].fields_by_name['file']._loaded_options = None + _globals['_EGRESSINFO'].fields_by_name['file']._serialized_options = b'\030\001' + _globals['_EGRESSINFO'].fields_by_name['segments']._loaded_options = None + _globals['_EGRESSINFO'].fields_by_name['segments']._serialized_options = b'\030\001' + _globals['_EXPORTREPLAYREQUEST'].fields_by_name['replay_id']._loaded_options = None + _globals['_EXPORTREPLAYREQUEST'].fields_by_name['replay_id']._serialized_options = b'\272P\010replayID' + _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._loaded_options = None _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._serialized_options = b'\030\001' - _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._options = None + _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._loaded_options = None _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._serialized_options = b'\030\001' - _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._options = None + _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._loaded_options = None _globals['_ROOMCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._serialized_options = b'\030\001' - _globals['_WEBEGRESSREQUEST'].fields_by_name['file']._options = None + _globals['_WEBEGRESSREQUEST'].fields_by_name['file']._loaded_options = None _globals['_WEBEGRESSREQUEST'].fields_by_name['file']._serialized_options = b'\030\001' - _globals['_WEBEGRESSREQUEST'].fields_by_name['stream']._options = None + _globals['_WEBEGRESSREQUEST'].fields_by_name['stream']._loaded_options = None _globals['_WEBEGRESSREQUEST'].fields_by_name['stream']._serialized_options = b'\030\001' - _globals['_WEBEGRESSREQUEST'].fields_by_name['segments']._options = None + _globals['_WEBEGRESSREQUEST'].fields_by_name['segments']._loaded_options = None _globals['_WEBEGRESSREQUEST'].fields_by_name['segments']._serialized_options = b'\030\001' - _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._options = None + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['audio_track_id']._loaded_options = None + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['audio_track_id']._serialized_options = b'\272P\014audioTrackID' + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['video_track_id']._loaded_options = None + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['video_track_id']._serialized_options = b'\272P\014videoTrackID' + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._loaded_options = None _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['file']._serialized_options = b'\030\001' - _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._options = None + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._loaded_options = None _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['stream']._serialized_options = b'\030\001' - _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._options = None + _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._loaded_options = None _globals['_TRACKCOMPOSITEEGRESSREQUEST'].fields_by_name['segments']._serialized_options = b'\030\001' - _globals['_S3UPLOAD_METADATAENTRY']._options = None - _globals['_S3UPLOAD_METADATAENTRY']._serialized_options = b'8\001' - _globals['_EGRESSINFO'].fields_by_name['stream']._options = None - _globals['_EGRESSINFO'].fields_by_name['stream']._serialized_options = b'\030\001' - _globals['_EGRESSINFO'].fields_by_name['file']._options = None - _globals['_EGRESSINFO'].fields_by_name['file']._serialized_options = b'\030\001' - _globals['_EGRESSINFO'].fields_by_name['segments']._options = None - _globals['_EGRESSINFO'].fields_by_name['segments']._serialized_options = b'\030\001' - _globals['_STREAMINFOLIST']._options = None - _globals['_STREAMINFOLIST']._serialized_options = b'\030\001' - _globals['_ENCODEDFILETYPE']._serialized_start=6929 - _globals['_ENCODEDFILETYPE']._serialized_end=6986 - _globals['_SEGMENTEDFILEPROTOCOL']._serialized_start=6988 - _globals['_SEGMENTEDFILEPROTOCOL']._serialized_end=7066 - _globals['_SEGMENTEDFILESUFFIX']._serialized_start=7068 - _globals['_SEGMENTEDFILESUFFIX']._serialized_end=7115 - _globals['_IMAGEFILESUFFIX']._serialized_start=7117 - _globals['_IMAGEFILESUFFIX']._serialized_end=7186 - _globals['_STREAMPROTOCOL']._serialized_start=7188 - _globals['_STREAMPROTOCOL']._serialized_end=7236 - _globals['_ENCODINGOPTIONSPRESET']._serialized_start=7239 - _globals['_ENCODINGOPTIONSPRESET']._serialized_end=7446 - _globals['_EGRESSSTATUS']._serialized_start=7449 - _globals['_EGRESSSTATUS']._serialized_end=7608 - _globals['_ROOMCOMPOSITEEGRESSREQUEST']._serialized_start=56 - _globals['_ROOMCOMPOSITEEGRESSREQUEST']._serialized_end=645 - _globals['_WEBEGRESSREQUEST']._serialized_start=648 - _globals['_WEBEGRESSREQUEST']._serialized_end=1208 - _globals['_PARTICIPANTEGRESSREQUEST']._serialized_start=1211 - _globals['_PARTICIPANTEGRESSREQUEST']._serialized_end=1600 - _globals['_TRACKCOMPOSITEEGRESSREQUEST']._serialized_start=1603 - _globals['_TRACKCOMPOSITEEGRESSREQUEST']._serialized_end=2160 - _globals['_TRACKEGRESSREQUEST']._serialized_start=2163 - _globals['_TRACKEGRESSREQUEST']._serialized_end=2298 - _globals['_ENCODEDFILEOUTPUT']._serialized_start=2301 - _globals['_ENCODEDFILEOUTPUT']._serialized_end=2571 - _globals['_SEGMENTEDFILEOUTPUT']._serialized_start=2574 - _globals['_SEGMENTEDFILEOUTPUT']._serialized_end=2990 - _globals['_DIRECTFILEOUTPUT']._serialized_start=2993 - _globals['_DIRECTFILEOUTPUT']._serialized_end=3217 - _globals['_IMAGEOUTPUT']._serialized_start=3220 - _globals['_IMAGEOUTPUT']._serialized_end=3596 - _globals['_S3UPLOAD']._serialized_start=3599 - _globals['_S3UPLOAD']._serialized_end=3927 - _globals['_S3UPLOAD_METADATAENTRY']._serialized_start=3880 - _globals['_S3UPLOAD_METADATAENTRY']._serialized_end=3927 - _globals['_GCPUPLOAD']._serialized_start=3929 - _globals['_GCPUPLOAD']._serialized_end=4014 - _globals['_AZUREBLOBUPLOAD']._serialized_start=4016 - _globals['_AZUREBLOBUPLOAD']._serialized_end=4100 - _globals['_ALIOSSUPLOAD']._serialized_start=4102 - _globals['_ALIOSSUPLOAD']._serialized_end=4202 - _globals['_PROXYCONFIG']._serialized_start=4204 - _globals['_PROXYCONFIG']._serialized_end=4266 - _globals['_STREAMOUTPUT']._serialized_start=4268 - _globals['_STREAMOUTPUT']._serialized_end=4339 - _globals['_ENCODINGOPTIONS']._serialized_start=4342 - _globals['_ENCODINGOPTIONS']._serialized_end=4653 - _globals['_UPDATELAYOUTREQUEST']._serialized_start=4655 - _globals['_UPDATELAYOUTREQUEST']._serialized_end=4711 - _globals['_UPDATESTREAMREQUEST']._serialized_start=4713 - _globals['_UPDATESTREAMREQUEST']._serialized_end=4806 - _globals['_LISTEGRESSREQUEST']._serialized_start=4808 - _globals['_LISTEGRESSREQUEST']._serialized_end=4881 - _globals['_LISTEGRESSRESPONSE']._serialized_start=4883 - _globals['_LISTEGRESSRESPONSE']._serialized_end=4939 - _globals['_STOPEGRESSREQUEST']._serialized_start=4941 - _globals['_STOPEGRESSREQUEST']._serialized_end=4979 - _globals['_EGRESSINFO']._serialized_start=4982 - _globals['_EGRESSINFO']._serialized_end=5804 - _globals['_STREAMINFOLIST']._serialized_start=5806 - _globals['_STREAMINFOLIST']._serialized_end=5861 - _globals['_STREAMINFO']._serialized_start=5864 - _globals['_STREAMINFO']._serialized_end=6052 - _globals['_STREAMINFO_STATUS']._serialized_start=6006 - _globals['_STREAMINFO_STATUS']._serialized_end=6052 - _globals['_FILEINFO']._serialized_start=6054 - _globals['_FILEINFO']._serialized_end=6170 - _globals['_SEGMENTSINFO']._serialized_start=6173 - _globals['_SEGMENTSINFO']._serialized_end=6390 - _globals['_IMAGESINFO']._serialized_start=6392 - _globals['_IMAGESINFO']._serialized_end=6463 - _globals['_AUTOPARTICIPANTEGRESS']._serialized_start=6466 - _globals['_AUTOPARTICIPANTEGRESS']._serialized_end=6701 - _globals['_AUTOTRACKEGRESS']._serialized_start=6704 - _globals['_AUTOTRACKEGRESS']._serialized_end=6927 - _globals['_EGRESS']._serialized_start=7611 - _globals['_EGRESS']._serialized_end=8279 + _globals['_TRACKEGRESSREQUEST'].fields_by_name['track_id']._loaded_options = None + _globals['_TRACKEGRESSREQUEST'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_UPDATELAYOUTREQUEST'].fields_by_name['egress_id']._loaded_options = None + _globals['_UPDATELAYOUTREQUEST'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_UPDATESTREAMREQUEST'].fields_by_name['egress_id']._loaded_options = None + _globals['_UPDATESTREAMREQUEST'].fields_by_name['egress_id']._serialized_options = b'\272P\010egressID' + _globals['_AUDIOCHANNEL']._serialized_start=10219 + _globals['_AUDIOCHANNEL']._serialized_end=10306 + _globals['_ENCODINGOPTIONSPRESET']._serialized_start=10309 + _globals['_ENCODINGOPTIONSPRESET']._serialized_end=10516 + _globals['_ENCODEDFILETYPE']._serialized_start=10518 + _globals['_ENCODEDFILETYPE']._serialized_end=10584 + _globals['_STREAMPROTOCOL']._serialized_start=10586 + _globals['_STREAMPROTOCOL']._serialized_end=10658 + _globals['_SEGMENTEDFILEPROTOCOL']._serialized_start=10660 + _globals['_SEGMENTEDFILEPROTOCOL']._serialized_end=10738 + _globals['_SEGMENTEDFILESUFFIX']._serialized_start=10740 + _globals['_SEGMENTEDFILESUFFIX']._serialized_end=10787 + _globals['_IMAGEFILESUFFIX']._serialized_start=10789 + _globals['_IMAGEFILESUFFIX']._serialized_end=10891 + _globals['_EGRESSSOURCETYPE']._serialized_start=10893 + _globals['_EGRESSSOURCETYPE']._serialized_end=10967 + _globals['_EGRESSSTATUS']._serialized_start=10970 + _globals['_EGRESSSTATUS']._serialized_end=11129 + _globals['_AUDIOMIXING']._serialized_start=11131 + _globals['_AUDIOMIXING']._serialized_end=11216 + _globals['_STARTEGRESSREQUEST']._serialized_start=78 + _globals['_STARTEGRESSREQUEST']._serialized_end=471 + _globals['_TEMPLATESOURCE']._serialized_start=473 + _globals['_TEMPLATESOURCE']._serialized_end=570 + _globals['_WEBSOURCE']._serialized_start=572 + _globals['_WEBSOURCE']._serialized_end=664 + _globals['_MEDIASOURCE']._serialized_start=667 + _globals['_MEDIASOURCE']._serialized_end=825 + _globals['_PARTICIPANTVIDEO']._serialized_start=827 + _globals['_PARTICIPANTVIDEO']._serialized_end=892 + _globals['_AUDIOCONFIG']._serialized_start=894 + _globals['_AUDIOCONFIG']._serialized_end=965 + _globals['_AUDIOROUTE']._serialized_start=968 + _globals['_AUDIOROUTE']._serialized_end=1152 + _globals['_DATACONFIG']._serialized_start=1154 + _globals['_DATACONFIG']._serialized_end=1229 + _globals['_DATASELECTOR']._serialized_start=1231 + _globals['_DATASELECTOR']._serialized_end=1318 + _globals['_ENCODINGOPTIONS']._serialized_start=1321 + _globals['_ENCODINGOPTIONS']._serialized_end=1640 + _globals['_OUTPUT']._serialized_start=1643 + _globals['_OUTPUT']._serialized_end=1870 + _globals['_FILEOUTPUT']._serialized_start=1872 + _globals['_FILEOUTPUT']._serialized_end=1973 + _globals['_STREAMOUTPUT']._serialized_start=1975 + _globals['_STREAMOUTPUT']._serialized_end=2046 + _globals['_SEGMENTEDFILEOUTPUT']._serialized_start=2049 + _globals['_SEGMENTEDFILEOUTPUT']._serialized_end=2465 + _globals['_IMAGEOUTPUT']._serialized_start=2468 + _globals['_IMAGEOUTPUT']._serialized_end=2844 + _globals['_STORAGECONFIG']._serialized_start=2847 + _globals['_STORAGECONFIG']._serialized_end=3026 + _globals['_S3UPLOAD']._serialized_start=3029 + _globals['_S3UPLOAD']._serialized_end=3478 + _globals['_S3UPLOAD_METADATAENTRY']._serialized_start=3431 + _globals['_S3UPLOAD_METADATAENTRY']._serialized_end=3478 + _globals['_GCPUPLOAD']._serialized_start=3480 + _globals['_GCPUPLOAD']._serialized_end=3570 + _globals['_AZUREBLOBUPLOAD']._serialized_start=3572 + _globals['_AZUREBLOBUPLOAD']._serialized_end=3666 + _globals['_ALIOSSUPLOAD']._serialized_start=3668 + _globals['_ALIOSSUPLOAD']._serialized_end=3778 + _globals['_PROXYCONFIG']._serialized_start=3780 + _globals['_PROXYCONFIG']._serialized_end=3847 + _globals['_LISTEGRESSREQUEST']._serialized_start=3850 + _globals['_LISTEGRESSREQUEST']._serialized_end=3982 + _globals['_LISTEGRESSRESPONSE']._serialized_start=3984 + _globals['_LISTEGRESSRESPONSE']._serialized_end=4091 + _globals['_UPDATEEGRESSREQUEST']._serialized_start=4094 + _globals['_UPDATEEGRESSREQUEST']._serialized_end=4229 + _globals['_STOPEGRESSREQUEST']._serialized_start=4231 + _globals['_STOPEGRESSREQUEST']._serialized_end=4282 + _globals['_EGRESSINFO']._serialized_start=4285 + _globals['_EGRESSINFO']._serialized_end=5351 + _globals['_STREAMINFO']._serialized_start=5354 + _globals['_STREAMINFO']._serialized_end=5582 + _globals['_STREAMINFO_STATUS']._serialized_start=5536 + _globals['_STREAMINFO_STATUS']._serialized_end=5582 + _globals['_FILEINFO']._serialized_start=5584 + _globals['_FILEINFO']._serialized_end=5700 + _globals['_SEGMENTSINFO']._serialized_start=5703 + _globals['_SEGMENTSINFO']._serialized_end=5920 + _globals['_IMAGESINFO']._serialized_start=5922 + _globals['_IMAGESINFO']._serialized_end=6018 + _globals['_AUTOPARTICIPANTEGRESS']._serialized_start=6021 + _globals['_AUTOPARTICIPANTEGRESS']._serialized_end=6256 + _globals['_AUTOTRACKEGRESS']._serialized_start=6259 + _globals['_AUTOTRACKEGRESS']._serialized_end=6482 + _globals['_EXPORTREPLAYREQUEST']._serialized_start=6485 + _globals['_EXPORTREPLAYREQUEST']._serialized_end=6940 + _globals['_ROOMCOMPOSITEEGRESSREQUEST']._serialized_start=6943 + _globals['_ROOMCOMPOSITEEGRESSREQUEST']._serialized_end=7618 + _globals['_WEBEGRESSREQUEST']._serialized_start=7621 + _globals['_WEBEGRESSREQUEST']._serialized_end=8223 + _globals['_PARTICIPANTEGRESSREQUEST']._serialized_start=8226 + _globals['_PARTICIPANTEGRESSREQUEST']._serialized_end=8657 + _globals['_TRACKCOMPOSITEEGRESSREQUEST']._serialized_start=8660 + _globals['_TRACKCOMPOSITEEGRESSREQUEST']._serialized_end=9293 + _globals['_TRACKEGRESSREQUEST']._serialized_start=9296 + _globals['_TRACKEGRESSREQUEST']._serialized_end=9485 + _globals['_DIRECTFILEOUTPUT']._serialized_start=9488 + _globals['_DIRECTFILEOUTPUT']._serialized_end=9712 + _globals['_ENCODEDFILEOUTPUT']._serialized_start=9715 + _globals['_ENCODEDFILEOUTPUT']._serialized_end=9985 + _globals['_UPDATELAYOUTREQUEST']._serialized_start=9987 + _globals['_UPDATELAYOUTREQUEST']._serialized_end=10056 + _globals['_UPDATESTREAMREQUEST']._serialized_start=10058 + _globals['_UPDATESTREAMREQUEST']._serialized_end=10164 + _globals['_STREAMINFOLIST']._serialized_start=10166 + _globals['_STREAMINFOLIST']._serialized_end=10217 + _globals['_EGRESS']._serialized_start=11219 + _globals['_EGRESS']._serialized_end=11952 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/egress.pyi b/livekit-protocol/livekit/protocol/egress.pyi index 50d0ed2f..0f021afd 100644 --- a/livekit-protocol/livekit/protocol/egress.pyi +++ b/livekit-protocol/livekit/protocol/egress.pyi @@ -1,4 +1,5 @@ from . import models as _models +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -7,11 +8,36 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor +class AudioChannel(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + AUDIO_CHANNEL_BOTH: _ClassVar[AudioChannel] + AUDIO_CHANNEL_LEFT: _ClassVar[AudioChannel] + AUDIO_CHANNEL_RIGHT: _ClassVar[AudioChannel] + +class EncodingOptionsPreset(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + H264_720P_30: _ClassVar[EncodingOptionsPreset] + H264_720P_60: _ClassVar[EncodingOptionsPreset] + H264_1080P_30: _ClassVar[EncodingOptionsPreset] + H264_1080P_60: _ClassVar[EncodingOptionsPreset] + PORTRAIT_H264_720P_30: _ClassVar[EncodingOptionsPreset] + PORTRAIT_H264_720P_60: _ClassVar[EncodingOptionsPreset] + PORTRAIT_H264_1080P_30: _ClassVar[EncodingOptionsPreset] + PORTRAIT_H264_1080P_60: _ClassVar[EncodingOptionsPreset] + class EncodedFileType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () DEFAULT_FILETYPE: _ClassVar[EncodedFileType] MP4: _ClassVar[EncodedFileType] OGG: _ClassVar[EncodedFileType] + MP3: _ClassVar[EncodedFileType] + +class StreamProtocol(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DEFAULT_PROTOCOL: _ClassVar[StreamProtocol] + RTMP: _ClassVar[StreamProtocol] + SRT: _ClassVar[StreamProtocol] + WEBSOCKET: _ClassVar[StreamProtocol] class SegmentedFileProtocol(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -27,22 +53,12 @@ class ImageFileSuffix(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () IMAGE_SUFFIX_INDEX: _ClassVar[ImageFileSuffix] IMAGE_SUFFIX_TIMESTAMP: _ClassVar[ImageFileSuffix] + IMAGE_SUFFIX_NONE_OVERWRITE: _ClassVar[ImageFileSuffix] -class StreamProtocol(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): - __slots__ = () - DEFAULT_PROTOCOL: _ClassVar[StreamProtocol] - RTMP: _ClassVar[StreamProtocol] - -class EncodingOptionsPreset(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): +class EgressSourceType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () - H264_720P_30: _ClassVar[EncodingOptionsPreset] - H264_720P_60: _ClassVar[EncodingOptionsPreset] - H264_1080P_30: _ClassVar[EncodingOptionsPreset] - H264_1080P_60: _ClassVar[EncodingOptionsPreset] - PORTRAIT_H264_720P_30: _ClassVar[EncodingOptionsPreset] - PORTRAIT_H264_720P_60: _ClassVar[EncodingOptionsPreset] - PORTRAIT_H264_1080P_30: _ClassVar[EncodingOptionsPreset] - PORTRAIT_H264_1080P_60: _ClassVar[EncodingOptionsPreset] + EGRESS_SOURCE_TYPE_WEB: _ClassVar[EgressSourceType] + EGRESS_SOURCE_TYPE_SDK: _ClassVar[EgressSourceType] class EgressStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -53,25 +69,40 @@ class EgressStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): EGRESS_FAILED: _ClassVar[EgressStatus] EGRESS_ABORTED: _ClassVar[EgressStatus] EGRESS_LIMIT_REACHED: _ClassVar[EgressStatus] + +class AudioMixing(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DEFAULT_MIXING: _ClassVar[AudioMixing] + DUAL_CHANNEL_AGENT: _ClassVar[AudioMixing] + DUAL_CHANNEL_ALTERNATE: _ClassVar[AudioMixing] +AUDIO_CHANNEL_BOTH: AudioChannel +AUDIO_CHANNEL_LEFT: AudioChannel +AUDIO_CHANNEL_RIGHT: AudioChannel +H264_720P_30: EncodingOptionsPreset +H264_720P_60: EncodingOptionsPreset +H264_1080P_30: EncodingOptionsPreset +H264_1080P_60: EncodingOptionsPreset +PORTRAIT_H264_720P_30: EncodingOptionsPreset +PORTRAIT_H264_720P_60: EncodingOptionsPreset +PORTRAIT_H264_1080P_30: EncodingOptionsPreset +PORTRAIT_H264_1080P_60: EncodingOptionsPreset DEFAULT_FILETYPE: EncodedFileType MP4: EncodedFileType OGG: EncodedFileType +MP3: EncodedFileType +DEFAULT_PROTOCOL: StreamProtocol +RTMP: StreamProtocol +SRT: StreamProtocol +WEBSOCKET: StreamProtocol DEFAULT_SEGMENTED_FILE_PROTOCOL: SegmentedFileProtocol HLS_PROTOCOL: SegmentedFileProtocol INDEX: SegmentedFileSuffix TIMESTAMP: SegmentedFileSuffix IMAGE_SUFFIX_INDEX: ImageFileSuffix IMAGE_SUFFIX_TIMESTAMP: ImageFileSuffix -DEFAULT_PROTOCOL: StreamProtocol -RTMP: StreamProtocol -H264_720P_30: EncodingOptionsPreset -H264_720P_60: EncodingOptionsPreset -H264_1080P_30: EncodingOptionsPreset -H264_1080P_60: EncodingOptionsPreset -PORTRAIT_H264_720P_30: EncodingOptionsPreset -PORTRAIT_H264_720P_60: EncodingOptionsPreset -PORTRAIT_H264_1080P_30: EncodingOptionsPreset -PORTRAIT_H264_1080P_60: EncodingOptionsPreset +IMAGE_SUFFIX_NONE_OVERWRITE: ImageFileSuffix +EGRESS_SOURCE_TYPE_WEB: EgressSourceType +EGRESS_SOURCE_TYPE_SDK: EgressSourceType EGRESS_STARTING: EgressStatus EGRESS_ACTIVE: EgressStatus EGRESS_ENDING: EgressStatus @@ -79,148 +110,169 @@ EGRESS_COMPLETE: EgressStatus EGRESS_FAILED: EgressStatus EGRESS_ABORTED: EgressStatus EGRESS_LIMIT_REACHED: EgressStatus +DEFAULT_MIXING: AudioMixing +DUAL_CHANNEL_AGENT: AudioMixing +DUAL_CHANNEL_ALTERNATE: AudioMixing -class RoomCompositeEgressRequest(_message.Message): - __slots__ = ("room_name", "layout", "audio_only", "video_only", "custom_base_url", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs") +class StartEgressRequest(_message.Message): + __slots__ = ("room_name", "template", "web", "media", "preset", "advanced", "outputs", "storage", "webhooks") ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + TEMPLATE_FIELD_NUMBER: _ClassVar[int] + WEB_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STORAGE_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + room_name: str + template: TemplateSource + web: WebSource + media: MediaSource + preset: EncodingOptionsPreset + advanced: EncodingOptions + outputs: _containers.RepeatedCompositeFieldContainer[Output] + storage: StorageConfig + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, room_name: _Optional[str] = ..., template: _Optional[_Union[TemplateSource, _Mapping]] = ..., web: _Optional[_Union[WebSource, _Mapping]] = ..., media: _Optional[_Union[MediaSource, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., outputs: _Optional[_Iterable[_Union[Output, _Mapping]]] = ..., storage: _Optional[_Union[StorageConfig, _Mapping]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class TemplateSource(_message.Message): + __slots__ = ("layout", "audio_only", "video_only", "custom_base_url") LAYOUT_FIELD_NUMBER: _ClassVar[int] AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] VIDEO_ONLY_FIELD_NUMBER: _ClassVar[int] CUSTOM_BASE_URL_FIELD_NUMBER: _ClassVar[int] - FILE_FIELD_NUMBER: _ClassVar[int] - STREAM_FIELD_NUMBER: _ClassVar[int] - SEGMENTS_FIELD_NUMBER: _ClassVar[int] - PRESET_FIELD_NUMBER: _ClassVar[int] - ADVANCED_FIELD_NUMBER: _ClassVar[int] - FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - room_name: str layout: str audio_only: bool video_only: bool custom_base_url: str - file: EncodedFileOutput - stream: StreamOutput - segments: SegmentedFileOutput - preset: EncodingOptionsPreset - advanced: EncodingOptions - file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] - stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] - segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] - image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] - def __init__(self, room_name: _Optional[str] = ..., layout: _Optional[str] = ..., audio_only: bool = ..., video_only: bool = ..., custom_base_url: _Optional[str] = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ...) -> None: ... + def __init__(self, layout: _Optional[str] = ..., audio_only: bool = ..., video_only: bool = ..., custom_base_url: _Optional[str] = ...) -> None: ... -class WebEgressRequest(_message.Message): - __slots__ = ("url", "audio_only", "video_only", "await_start_signal", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs") +class WebSource(_message.Message): + __slots__ = ("url", "audio_only", "video_only", "await_start_signal") URL_FIELD_NUMBER: _ClassVar[int] AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] VIDEO_ONLY_FIELD_NUMBER: _ClassVar[int] AWAIT_START_SIGNAL_FIELD_NUMBER: _ClassVar[int] - FILE_FIELD_NUMBER: _ClassVar[int] - STREAM_FIELD_NUMBER: _ClassVar[int] - SEGMENTS_FIELD_NUMBER: _ClassVar[int] - PRESET_FIELD_NUMBER: _ClassVar[int] - ADVANCED_FIELD_NUMBER: _ClassVar[int] - FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] url: str audio_only: bool video_only: bool await_start_signal: bool - file: EncodedFileOutput - stream: StreamOutput - segments: SegmentedFileOutput - preset: EncodingOptionsPreset - advanced: EncodingOptions - file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] - stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] - segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] - image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] - def __init__(self, url: _Optional[str] = ..., audio_only: bool = ..., video_only: bool = ..., await_start_signal: bool = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ...) -> None: ... + def __init__(self, url: _Optional[str] = ..., audio_only: bool = ..., video_only: bool = ..., await_start_signal: bool = ...) -> None: ... -class ParticipantEgressRequest(_message.Message): - __slots__ = ("room_name", "identity", "screen_share", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs") - ROOM_NAME_FIELD_NUMBER: _ClassVar[int] +class MediaSource(_message.Message): + __slots__ = ("video_track_id", "participant_video", "audio") + VIDEO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_VIDEO_FIELD_NUMBER: _ClassVar[int] + AUDIO_FIELD_NUMBER: _ClassVar[int] + video_track_id: str + participant_video: ParticipantVideo + audio: AudioConfig + def __init__(self, video_track_id: _Optional[str] = ..., participant_video: _Optional[_Union[ParticipantVideo, _Mapping]] = ..., audio: _Optional[_Union[AudioConfig, _Mapping]] = ...) -> None: ... + +class ParticipantVideo(_message.Message): + __slots__ = ("identity", "prefer_screen_share") IDENTITY_FIELD_NUMBER: _ClassVar[int] - SCREEN_SHARE_FIELD_NUMBER: _ClassVar[int] - PRESET_FIELD_NUMBER: _ClassVar[int] - ADVANCED_FIELD_NUMBER: _ClassVar[int] - FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - room_name: str + PREFER_SCREEN_SHARE_FIELD_NUMBER: _ClassVar[int] identity: str - screen_share: bool - preset: EncodingOptionsPreset - advanced: EncodingOptions - file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] - stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] - segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] - image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] - def __init__(self, room_name: _Optional[str] = ..., identity: _Optional[str] = ..., screen_share: bool = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ...) -> None: ... + prefer_screen_share: bool + def __init__(self, identity: _Optional[str] = ..., prefer_screen_share: bool = ...) -> None: ... + +class AudioConfig(_message.Message): + __slots__ = ("capture_all", "routes") + CAPTURE_ALL_FIELD_NUMBER: _ClassVar[int] + ROUTES_FIELD_NUMBER: _ClassVar[int] + capture_all: bool + routes: _containers.RepeatedCompositeFieldContainer[AudioRoute] + def __init__(self, capture_all: bool = ..., routes: _Optional[_Iterable[_Union[AudioRoute, _Mapping]]] = ...) -> None: ... + +class AudioRoute(_message.Message): + __slots__ = ("track_id", "participant_identity", "participant_kind", "channel") + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_KIND_FIELD_NUMBER: _ClassVar[int] + CHANNEL_FIELD_NUMBER: _ClassVar[int] + track_id: str + participant_identity: str + participant_kind: _models.ParticipantInfo.Kind + channel: AudioChannel + def __init__(self, track_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_kind: _Optional[_Union[_models.ParticipantInfo.Kind, str]] = ..., channel: _Optional[_Union[AudioChannel, str]] = ...) -> None: ... + +class DataConfig(_message.Message): + __slots__ = ("capture_all", "selectors") + CAPTURE_ALL_FIELD_NUMBER: _ClassVar[int] + SELECTORS_FIELD_NUMBER: _ClassVar[int] + capture_all: bool + selectors: _containers.RepeatedCompositeFieldContainer[DataSelector] + def __init__(self, capture_all: bool = ..., selectors: _Optional[_Iterable[_Union[DataSelector, _Mapping]]] = ...) -> None: ... + +class DataSelector(_message.Message): + __slots__ = ("track_id", "participant_identity") + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + track_id: str + participant_identity: str + def __init__(self, track_id: _Optional[str] = ..., participant_identity: _Optional[str] = ...) -> None: ... -class TrackCompositeEgressRequest(_message.Message): - __slots__ = ("room_name", "audio_track_id", "video_track_id", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs") - ROOM_NAME_FIELD_NUMBER: _ClassVar[int] - AUDIO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] - VIDEO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] +class EncodingOptions(_message.Message): + __slots__ = ("width", "height", "depth", "framerate", "audio_codec", "audio_bitrate", "audio_frequency", "video_codec", "video_bitrate", "key_frame_interval", "audio_quality", "video_quality") + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + DEPTH_FIELD_NUMBER: _ClassVar[int] + FRAMERATE_FIELD_NUMBER: _ClassVar[int] + AUDIO_CODEC_FIELD_NUMBER: _ClassVar[int] + AUDIO_BITRATE_FIELD_NUMBER: _ClassVar[int] + AUDIO_FREQUENCY_FIELD_NUMBER: _ClassVar[int] + VIDEO_CODEC_FIELD_NUMBER: _ClassVar[int] + VIDEO_BITRATE_FIELD_NUMBER: _ClassVar[int] + KEY_FRAME_INTERVAL_FIELD_NUMBER: _ClassVar[int] + AUDIO_QUALITY_FIELD_NUMBER: _ClassVar[int] + VIDEO_QUALITY_FIELD_NUMBER: _ClassVar[int] + width: int + height: int + depth: int + framerate: int + audio_codec: _models.AudioCodec + audio_bitrate: int + audio_frequency: int + video_codec: _models.VideoCodec + video_bitrate: int + key_frame_interval: float + audio_quality: int + video_quality: int + def __init__(self, width: _Optional[int] = ..., height: _Optional[int] = ..., depth: _Optional[int] = ..., framerate: _Optional[int] = ..., audio_codec: _Optional[_Union[_models.AudioCodec, str]] = ..., audio_bitrate: _Optional[int] = ..., audio_frequency: _Optional[int] = ..., video_codec: _Optional[_Union[_models.VideoCodec, str]] = ..., video_bitrate: _Optional[int] = ..., key_frame_interval: _Optional[float] = ..., audio_quality: _Optional[int] = ..., video_quality: _Optional[int] = ...) -> None: ... + +class Output(_message.Message): + __slots__ = ("file", "stream", "segments", "images", "storage") FILE_FIELD_NUMBER: _ClassVar[int] STREAM_FIELD_NUMBER: _ClassVar[int] SEGMENTS_FIELD_NUMBER: _ClassVar[int] - PRESET_FIELD_NUMBER: _ClassVar[int] - ADVANCED_FIELD_NUMBER: _ClassVar[int] - FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] - room_name: str - audio_track_id: str - video_track_id: str - file: EncodedFileOutput + IMAGES_FIELD_NUMBER: _ClassVar[int] + STORAGE_FIELD_NUMBER: _ClassVar[int] + file: FileOutput stream: StreamOutput segments: SegmentedFileOutput - preset: EncodingOptionsPreset - advanced: EncodingOptions - file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] - stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] - segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] - image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] - def __init__(self, room_name: _Optional[str] = ..., audio_track_id: _Optional[str] = ..., video_track_id: _Optional[str] = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ...) -> None: ... + images: ImageOutput + storage: StorageConfig + def __init__(self, file: _Optional[_Union[FileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., images: _Optional[_Union[ImageOutput, _Mapping]] = ..., storage: _Optional[_Union[StorageConfig, _Mapping]] = ...) -> None: ... -class TrackEgressRequest(_message.Message): - __slots__ = ("room_name", "track_id", "file", "websocket_url") - ROOM_NAME_FIELD_NUMBER: _ClassVar[int] - TRACK_ID_FIELD_NUMBER: _ClassVar[int] - FILE_FIELD_NUMBER: _ClassVar[int] - WEBSOCKET_URL_FIELD_NUMBER: _ClassVar[int] - room_name: str - track_id: str - file: DirectFileOutput - websocket_url: str - def __init__(self, room_name: _Optional[str] = ..., track_id: _Optional[str] = ..., file: _Optional[_Union[DirectFileOutput, _Mapping]] = ..., websocket_url: _Optional[str] = ...) -> None: ... - -class EncodedFileOutput(_message.Message): - __slots__ = ("file_type", "filepath", "disable_manifest", "s3", "gcp", "azure", "aliOSS") +class FileOutput(_message.Message): + __slots__ = ("file_type", "filepath", "disable_manifest") FILE_TYPE_FIELD_NUMBER: _ClassVar[int] FILEPATH_FIELD_NUMBER: _ClassVar[int] DISABLE_MANIFEST_FIELD_NUMBER: _ClassVar[int] - S3_FIELD_NUMBER: _ClassVar[int] - GCP_FIELD_NUMBER: _ClassVar[int] - AZURE_FIELD_NUMBER: _ClassVar[int] - ALIOSS_FIELD_NUMBER: _ClassVar[int] file_type: EncodedFileType filepath: str disable_manifest: bool - s3: S3Upload - gcp: GCPUpload - azure: AzureBlobUpload - aliOSS: AliOSSUpload - def __init__(self, file_type: _Optional[_Union[EncodedFileType, str]] = ..., filepath: _Optional[str] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... + def __init__(self, file_type: _Optional[_Union[EncodedFileType, str]] = ..., filepath: _Optional[str] = ..., disable_manifest: bool = ...) -> None: ... + +class StreamOutput(_message.Message): + __slots__ = ("protocol", "urls") + PROTOCOL_FIELD_NUMBER: _ClassVar[int] + URLS_FIELD_NUMBER: _ClassVar[int] + protocol: StreamProtocol + urls: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, protocol: _Optional[_Union[StreamProtocol, str]] = ..., urls: _Optional[_Iterable[str]] = ...) -> None: ... class SegmentedFileOutput(_message.Message): __slots__ = ("protocol", "filename_prefix", "playlist_name", "live_playlist_name", "segment_duration", "filename_suffix", "disable_manifest", "s3", "gcp", "azure", "aliOSS") @@ -248,22 +300,6 @@ class SegmentedFileOutput(_message.Message): aliOSS: AliOSSUpload def __init__(self, protocol: _Optional[_Union[SegmentedFileProtocol, str]] = ..., filename_prefix: _Optional[str] = ..., playlist_name: _Optional[str] = ..., live_playlist_name: _Optional[str] = ..., segment_duration: _Optional[int] = ..., filename_suffix: _Optional[_Union[SegmentedFileSuffix, str]] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... -class DirectFileOutput(_message.Message): - __slots__ = ("filepath", "disable_manifest", "s3", "gcp", "azure", "aliOSS") - FILEPATH_FIELD_NUMBER: _ClassVar[int] - DISABLE_MANIFEST_FIELD_NUMBER: _ClassVar[int] - S3_FIELD_NUMBER: _ClassVar[int] - GCP_FIELD_NUMBER: _ClassVar[int] - AZURE_FIELD_NUMBER: _ClassVar[int] - ALIOSS_FIELD_NUMBER: _ClassVar[int] - filepath: str - disable_manifest: bool - s3: S3Upload - gcp: GCPUpload - azure: AzureBlobUpload - aliOSS: AliOSSUpload - def __init__(self, filepath: _Optional[str] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... - class ImageOutput(_message.Message): __slots__ = ("capture_interval", "width", "height", "filename_prefix", "filename_suffix", "image_codec", "disable_manifest", "s3", "gcp", "azure", "aliOSS") CAPTURE_INTERVAL_FIELD_NUMBER: _ClassVar[int] @@ -290,8 +326,20 @@ class ImageOutput(_message.Message): aliOSS: AliOSSUpload def __init__(self, capture_interval: _Optional[int] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., filename_prefix: _Optional[str] = ..., filename_suffix: _Optional[_Union[ImageFileSuffix, str]] = ..., image_codec: _Optional[_Union[_models.ImageCodec, str]] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... +class StorageConfig(_message.Message): + __slots__ = ("s3", "gcp", "azure", "aliOSS") + S3_FIELD_NUMBER: _ClassVar[int] + GCP_FIELD_NUMBER: _ClassVar[int] + AZURE_FIELD_NUMBER: _ClassVar[int] + ALIOSS_FIELD_NUMBER: _ClassVar[int] + s3: S3Upload + gcp: GCPUpload + azure: AzureBlobUpload + aliOSS: AliOSSUpload + def __init__(self, s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... + class S3Upload(_message.Message): - __slots__ = ("access_key", "secret", "session_token", "region", "endpoint", "bucket", "force_path_style", "metadata", "tagging", "content_disposition", "proxy") + __slots__ = ("access_key", "secret", "session_token", "assume_role_arn", "assume_role_external_id", "region", "endpoint", "bucket", "force_path_style", "metadata", "tagging", "content_disposition", "proxy") class MetadataEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -302,6 +350,8 @@ class S3Upload(_message.Message): ACCESS_KEY_FIELD_NUMBER: _ClassVar[int] SECRET_FIELD_NUMBER: _ClassVar[int] SESSION_TOKEN_FIELD_NUMBER: _ClassVar[int] + ASSUME_ROLE_ARN_FIELD_NUMBER: _ClassVar[int] + ASSUME_ROLE_EXTERNAL_ID_FIELD_NUMBER: _ClassVar[int] REGION_FIELD_NUMBER: _ClassVar[int] ENDPOINT_FIELD_NUMBER: _ClassVar[int] BUCKET_FIELD_NUMBER: _ClassVar[int] @@ -313,6 +363,8 @@ class S3Upload(_message.Message): access_key: str secret: str session_token: str + assume_role_arn: str + assume_role_external_id: str region: str endpoint: str bucket: str @@ -321,7 +373,7 @@ class S3Upload(_message.Message): tagging: str content_disposition: str proxy: ProxyConfig - def __init__(self, access_key: _Optional[str] = ..., secret: _Optional[str] = ..., session_token: _Optional[str] = ..., region: _Optional[str] = ..., endpoint: _Optional[str] = ..., bucket: _Optional[str] = ..., force_path_style: bool = ..., metadata: _Optional[_Mapping[str, str]] = ..., tagging: _Optional[str] = ..., content_disposition: _Optional[str] = ..., proxy: _Optional[_Union[ProxyConfig, _Mapping]] = ...) -> None: ... + def __init__(self, access_key: _Optional[str] = ..., secret: _Optional[str] = ..., session_token: _Optional[str] = ..., assume_role_arn: _Optional[str] = ..., assume_role_external_id: _Optional[str] = ..., region: _Optional[str] = ..., endpoint: _Optional[str] = ..., bucket: _Optional[str] = ..., force_path_style: bool = ..., metadata: _Optional[_Mapping[str, str]] = ..., tagging: _Optional[str] = ..., content_disposition: _Optional[str] = ..., proxy: _Optional[_Union[ProxyConfig, _Mapping]] = ...) -> None: ... class GCPUpload(_message.Message): __slots__ = ("credentials", "bucket", "proxy") @@ -367,75 +419,39 @@ class ProxyConfig(_message.Message): password: str def __init__(self, url: _Optional[str] = ..., username: _Optional[str] = ..., password: _Optional[str] = ...) -> None: ... -class StreamOutput(_message.Message): - __slots__ = ("protocol", "urls") - PROTOCOL_FIELD_NUMBER: _ClassVar[int] - URLS_FIELD_NUMBER: _ClassVar[int] - protocol: StreamProtocol - urls: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, protocol: _Optional[_Union[StreamProtocol, str]] = ..., urls: _Optional[_Iterable[str]] = ...) -> None: ... - -class EncodingOptions(_message.Message): - __slots__ = ("width", "height", "depth", "framerate", "audio_codec", "audio_bitrate", "audio_quality", "audio_frequency", "video_codec", "video_bitrate", "video_quality", "key_frame_interval") - WIDTH_FIELD_NUMBER: _ClassVar[int] - HEIGHT_FIELD_NUMBER: _ClassVar[int] - DEPTH_FIELD_NUMBER: _ClassVar[int] - FRAMERATE_FIELD_NUMBER: _ClassVar[int] - AUDIO_CODEC_FIELD_NUMBER: _ClassVar[int] - AUDIO_BITRATE_FIELD_NUMBER: _ClassVar[int] - AUDIO_QUALITY_FIELD_NUMBER: _ClassVar[int] - AUDIO_FREQUENCY_FIELD_NUMBER: _ClassVar[int] - VIDEO_CODEC_FIELD_NUMBER: _ClassVar[int] - VIDEO_BITRATE_FIELD_NUMBER: _ClassVar[int] - VIDEO_QUALITY_FIELD_NUMBER: _ClassVar[int] - KEY_FRAME_INTERVAL_FIELD_NUMBER: _ClassVar[int] - width: int - height: int - depth: int - framerate: int - audio_codec: _models.AudioCodec - audio_bitrate: int - audio_quality: int - audio_frequency: int - video_codec: _models.VideoCodec - video_bitrate: int - video_quality: int - key_frame_interval: float - def __init__(self, width: _Optional[int] = ..., height: _Optional[int] = ..., depth: _Optional[int] = ..., framerate: _Optional[int] = ..., audio_codec: _Optional[_Union[_models.AudioCodec, str]] = ..., audio_bitrate: _Optional[int] = ..., audio_quality: _Optional[int] = ..., audio_frequency: _Optional[int] = ..., video_codec: _Optional[_Union[_models.VideoCodec, str]] = ..., video_bitrate: _Optional[int] = ..., video_quality: _Optional[int] = ..., key_frame_interval: _Optional[float] = ...) -> None: ... - -class UpdateLayoutRequest(_message.Message): - __slots__ = ("egress_id", "layout") - EGRESS_ID_FIELD_NUMBER: _ClassVar[int] - LAYOUT_FIELD_NUMBER: _ClassVar[int] - egress_id: str - layout: str - def __init__(self, egress_id: _Optional[str] = ..., layout: _Optional[str] = ...) -> None: ... - -class UpdateStreamRequest(_message.Message): - __slots__ = ("egress_id", "add_output_urls", "remove_output_urls") - EGRESS_ID_FIELD_NUMBER: _ClassVar[int] - ADD_OUTPUT_URLS_FIELD_NUMBER: _ClassVar[int] - REMOVE_OUTPUT_URLS_FIELD_NUMBER: _ClassVar[int] - egress_id: str - add_output_urls: _containers.RepeatedScalarFieldContainer[str] - remove_output_urls: _containers.RepeatedScalarFieldContainer[str] - def __init__(self, egress_id: _Optional[str] = ..., add_output_urls: _Optional[_Iterable[str]] = ..., remove_output_urls: _Optional[_Iterable[str]] = ...) -> None: ... - class ListEgressRequest(_message.Message): - __slots__ = ("room_name", "egress_id", "active") + __slots__ = ("room_name", "egress_id", "active", "page_token") ROOM_NAME_FIELD_NUMBER: _ClassVar[int] EGRESS_ID_FIELD_NUMBER: _ClassVar[int] ACTIVE_FIELD_NUMBER: _ClassVar[int] + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] room_name: str egress_id: str active: bool - def __init__(self, room_name: _Optional[str] = ..., egress_id: _Optional[str] = ..., active: bool = ...) -> None: ... + page_token: _models.TokenPagination + def __init__(self, room_name: _Optional[str] = ..., egress_id: _Optional[str] = ..., active: bool = ..., page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ...) -> None: ... class ListEgressResponse(_message.Message): - __slots__ = ("items",) + __slots__ = ("items", "next_page_token") ITEMS_FIELD_NUMBER: _ClassVar[int] + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] items: _containers.RepeatedCompositeFieldContainer[EgressInfo] - def __init__(self, items: _Optional[_Iterable[_Union[EgressInfo, _Mapping]]] = ...) -> None: ... + next_page_token: _models.TokenPagination + def __init__(self, items: _Optional[_Iterable[_Union[EgressInfo, _Mapping]]] = ..., next_page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ...) -> None: ... + +class UpdateEgressRequest(_message.Message): + __slots__ = ("egress_id", "url", "layout", "add_stream_urls", "remove_stream_urls") + EGRESS_ID_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + LAYOUT_FIELD_NUMBER: _ClassVar[int] + ADD_STREAM_URLS_FIELD_NUMBER: _ClassVar[int] + REMOVE_STREAM_URLS_FIELD_NUMBER: _ClassVar[int] + egress_id: str + url: str + layout: str + add_stream_urls: _containers.RepeatedScalarFieldContainer[str] + remove_stream_urls: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, egress_id: _Optional[str] = ..., url: _Optional[str] = ..., layout: _Optional[str] = ..., add_stream_urls: _Optional[_Iterable[str]] = ..., remove_stream_urls: _Optional[_Iterable[str]] = ...) -> None: ... class StopEgressRequest(_message.Message): __slots__ = ("egress_id",) @@ -444,61 +460,67 @@ class StopEgressRequest(_message.Message): def __init__(self, egress_id: _Optional[str] = ...) -> None: ... class EgressInfo(_message.Message): - __slots__ = ("egress_id", "room_id", "room_name", "status", "started_at", "ended_at", "updated_at", "details", "error", "error_code", "room_composite", "web", "participant", "track_composite", "track", "stream", "file", "segments", "stream_results", "file_results", "segment_results", "image_results") + __slots__ = ("egress_id", "room_id", "room_name", "source_type", "status", "started_at", "ended_at", "updated_at", "egress", "replay", "room_composite", "web", "participant", "track_composite", "track", "stream_results", "file_results", "segment_results", "image_results", "error", "error_code", "details", "manifest_location", "backup_storage_used", "retry_count", "stream", "file", "segments") EGRESS_ID_FIELD_NUMBER: _ClassVar[int] ROOM_ID_FIELD_NUMBER: _ClassVar[int] ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + SOURCE_TYPE_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] STARTED_AT_FIELD_NUMBER: _ClassVar[int] ENDED_AT_FIELD_NUMBER: _ClassVar[int] UPDATED_AT_FIELD_NUMBER: _ClassVar[int] - DETAILS_FIELD_NUMBER: _ClassVar[int] - ERROR_FIELD_NUMBER: _ClassVar[int] - ERROR_CODE_FIELD_NUMBER: _ClassVar[int] + EGRESS_FIELD_NUMBER: _ClassVar[int] + REPLAY_FIELD_NUMBER: _ClassVar[int] ROOM_COMPOSITE_FIELD_NUMBER: _ClassVar[int] WEB_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_FIELD_NUMBER: _ClassVar[int] TRACK_COMPOSITE_FIELD_NUMBER: _ClassVar[int] TRACK_FIELD_NUMBER: _ClassVar[int] - STREAM_FIELD_NUMBER: _ClassVar[int] - FILE_FIELD_NUMBER: _ClassVar[int] - SEGMENTS_FIELD_NUMBER: _ClassVar[int] STREAM_RESULTS_FIELD_NUMBER: _ClassVar[int] FILE_RESULTS_FIELD_NUMBER: _ClassVar[int] SEGMENT_RESULTS_FIELD_NUMBER: _ClassVar[int] IMAGE_RESULTS_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + ERROR_CODE_FIELD_NUMBER: _ClassVar[int] + DETAILS_FIELD_NUMBER: _ClassVar[int] + MANIFEST_LOCATION_FIELD_NUMBER: _ClassVar[int] + BACKUP_STORAGE_USED_FIELD_NUMBER: _ClassVar[int] + RETRY_COUNT_FIELD_NUMBER: _ClassVar[int] + STREAM_FIELD_NUMBER: _ClassVar[int] + FILE_FIELD_NUMBER: _ClassVar[int] + SEGMENTS_FIELD_NUMBER: _ClassVar[int] egress_id: str room_id: str room_name: str + source_type: EgressSourceType status: EgressStatus started_at: int ended_at: int updated_at: int - details: str - error: str - error_code: int + egress: StartEgressRequest + replay: ExportReplayRequest room_composite: RoomCompositeEgressRequest web: WebEgressRequest participant: ParticipantEgressRequest track_composite: TrackCompositeEgressRequest track: TrackEgressRequest - stream: StreamInfoList - file: FileInfo - segments: SegmentsInfo stream_results: _containers.RepeatedCompositeFieldContainer[StreamInfo] file_results: _containers.RepeatedCompositeFieldContainer[FileInfo] segment_results: _containers.RepeatedCompositeFieldContainer[SegmentsInfo] image_results: _containers.RepeatedCompositeFieldContainer[ImagesInfo] - def __init__(self, egress_id: _Optional[str] = ..., room_id: _Optional[str] = ..., room_name: _Optional[str] = ..., status: _Optional[_Union[EgressStatus, str]] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., updated_at: _Optional[int] = ..., details: _Optional[str] = ..., error: _Optional[str] = ..., error_code: _Optional[int] = ..., room_composite: _Optional[_Union[RoomCompositeEgressRequest, _Mapping]] = ..., web: _Optional[_Union[WebEgressRequest, _Mapping]] = ..., participant: _Optional[_Union[ParticipantEgressRequest, _Mapping]] = ..., track_composite: _Optional[_Union[TrackCompositeEgressRequest, _Mapping]] = ..., track: _Optional[_Union[TrackEgressRequest, _Mapping]] = ..., stream: _Optional[_Union[StreamInfoList, _Mapping]] = ..., file: _Optional[_Union[FileInfo, _Mapping]] = ..., segments: _Optional[_Union[SegmentsInfo, _Mapping]] = ..., stream_results: _Optional[_Iterable[_Union[StreamInfo, _Mapping]]] = ..., file_results: _Optional[_Iterable[_Union[FileInfo, _Mapping]]] = ..., segment_results: _Optional[_Iterable[_Union[SegmentsInfo, _Mapping]]] = ..., image_results: _Optional[_Iterable[_Union[ImagesInfo, _Mapping]]] = ...) -> None: ... - -class StreamInfoList(_message.Message): - __slots__ = ("info",) - INFO_FIELD_NUMBER: _ClassVar[int] - info: _containers.RepeatedCompositeFieldContainer[StreamInfo] - def __init__(self, info: _Optional[_Iterable[_Union[StreamInfo, _Mapping]]] = ...) -> None: ... + error: str + error_code: int + details: str + manifest_location: str + backup_storage_used: bool + retry_count: int + stream: StreamInfoList + file: FileInfo + segments: SegmentsInfo + def __init__(self, egress_id: _Optional[str] = ..., room_id: _Optional[str] = ..., room_name: _Optional[str] = ..., source_type: _Optional[_Union[EgressSourceType, str]] = ..., status: _Optional[_Union[EgressStatus, str]] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., updated_at: _Optional[int] = ..., egress: _Optional[_Union[StartEgressRequest, _Mapping]] = ..., replay: _Optional[_Union[ExportReplayRequest, _Mapping]] = ..., room_composite: _Optional[_Union[RoomCompositeEgressRequest, _Mapping]] = ..., web: _Optional[_Union[WebEgressRequest, _Mapping]] = ..., participant: _Optional[_Union[ParticipantEgressRequest, _Mapping]] = ..., track_composite: _Optional[_Union[TrackCompositeEgressRequest, _Mapping]] = ..., track: _Optional[_Union[TrackEgressRequest, _Mapping]] = ..., stream_results: _Optional[_Iterable[_Union[StreamInfo, _Mapping]]] = ..., file_results: _Optional[_Iterable[_Union[FileInfo, _Mapping]]] = ..., segment_results: _Optional[_Iterable[_Union[SegmentsInfo, _Mapping]]] = ..., image_results: _Optional[_Iterable[_Union[ImagesInfo, _Mapping]]] = ..., error: _Optional[str] = ..., error_code: _Optional[int] = ..., details: _Optional[str] = ..., manifest_location: _Optional[str] = ..., backup_storage_used: bool = ..., retry_count: _Optional[int] = ..., stream: _Optional[_Union[StreamInfoList, _Mapping]] = ..., file: _Optional[_Union[FileInfo, _Mapping]] = ..., segments: _Optional[_Union[SegmentsInfo, _Mapping]] = ...) -> None: ... class StreamInfo(_message.Message): - __slots__ = ("url", "started_at", "ended_at", "duration", "status", "error") + __slots__ = ("url", "started_at", "ended_at", "duration", "status", "error", "last_retry_at", "retries") class Status(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () ACTIVE: _ClassVar[StreamInfo.Status] @@ -513,13 +535,17 @@ class StreamInfo(_message.Message): DURATION_FIELD_NUMBER: _ClassVar[int] STATUS_FIELD_NUMBER: _ClassVar[int] ERROR_FIELD_NUMBER: _ClassVar[int] + LAST_RETRY_AT_FIELD_NUMBER: _ClassVar[int] + RETRIES_FIELD_NUMBER: _ClassVar[int] url: str started_at: int ended_at: int duration: int status: StreamInfo.Status error: str - def __init__(self, url: _Optional[str] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., duration: _Optional[int] = ..., status: _Optional[_Union[StreamInfo.Status, str]] = ..., error: _Optional[str] = ...) -> None: ... + last_retry_at: int + retries: int + def __init__(self, url: _Optional[str] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., duration: _Optional[int] = ..., status: _Optional[_Union[StreamInfo.Status, str]] = ..., error: _Optional[str] = ..., last_retry_at: _Optional[int] = ..., retries: _Optional[int] = ...) -> None: ... class FileInfo(_message.Message): __slots__ = ("filename", "started_at", "ended_at", "duration", "size", "location") @@ -560,14 +586,16 @@ class SegmentsInfo(_message.Message): def __init__(self, playlist_name: _Optional[str] = ..., live_playlist_name: _Optional[str] = ..., duration: _Optional[int] = ..., size: _Optional[int] = ..., playlist_location: _Optional[str] = ..., live_playlist_location: _Optional[str] = ..., segment_count: _Optional[int] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ...) -> None: ... class ImagesInfo(_message.Message): - __slots__ = ("image_count", "started_at", "ended_at") + __slots__ = ("filename_prefix", "image_count", "started_at", "ended_at") + FILENAME_PREFIX_FIELD_NUMBER: _ClassVar[int] IMAGE_COUNT_FIELD_NUMBER: _ClassVar[int] STARTED_AT_FIELD_NUMBER: _ClassVar[int] ENDED_AT_FIELD_NUMBER: _ClassVar[int] + filename_prefix: str image_count: int started_at: int ended_at: int - def __init__(self, image_count: _Optional[int] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ...) -> None: ... + def __init__(self, filename_prefix: _Optional[str] = ..., image_count: _Optional[int] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ...) -> None: ... class AutoParticipantEgress(_message.Message): __slots__ = ("preset", "advanced", "file_outputs", "segment_outputs") @@ -596,3 +624,223 @@ class AutoTrackEgress(_message.Message): azure: AzureBlobUpload aliOSS: AliOSSUpload def __init__(self, filepath: _Optional[str] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... + +class ExportReplayRequest(_message.Message): + __slots__ = ("replay_id", "start_offset_ms", "end_offset_ms", "template", "web", "media", "preset", "advanced", "outputs", "storage", "webhooks") + REPLAY_ID_FIELD_NUMBER: _ClassVar[int] + START_OFFSET_MS_FIELD_NUMBER: _ClassVar[int] + END_OFFSET_MS_FIELD_NUMBER: _ClassVar[int] + TEMPLATE_FIELD_NUMBER: _ClassVar[int] + WEB_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STORAGE_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + replay_id: str + start_offset_ms: int + end_offset_ms: int + template: TemplateSource + web: WebSource + media: MediaSource + preset: EncodingOptionsPreset + advanced: EncodingOptions + outputs: _containers.RepeatedCompositeFieldContainer[Output] + storage: StorageConfig + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, replay_id: _Optional[str] = ..., start_offset_ms: _Optional[int] = ..., end_offset_ms: _Optional[int] = ..., template: _Optional[_Union[TemplateSource, _Mapping]] = ..., web: _Optional[_Union[WebSource, _Mapping]] = ..., media: _Optional[_Union[MediaSource, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., outputs: _Optional[_Iterable[_Union[Output, _Mapping]]] = ..., storage: _Optional[_Union[StorageConfig, _Mapping]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class RoomCompositeEgressRequest(_message.Message): + __slots__ = ("room_name", "layout", "audio_only", "audio_mixing", "video_only", "custom_base_url", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs", "webhooks") + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + LAYOUT_FIELD_NUMBER: _ClassVar[int] + AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] + AUDIO_MIXING_FIELD_NUMBER: _ClassVar[int] + VIDEO_ONLY_FIELD_NUMBER: _ClassVar[int] + CUSTOM_BASE_URL_FIELD_NUMBER: _ClassVar[int] + FILE_FIELD_NUMBER: _ClassVar[int] + STREAM_FIELD_NUMBER: _ClassVar[int] + SEGMENTS_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + room_name: str + layout: str + audio_only: bool + audio_mixing: AudioMixing + video_only: bool + custom_base_url: str + file: EncodedFileOutput + stream: StreamOutput + segments: SegmentedFileOutput + preset: EncodingOptionsPreset + advanced: EncodingOptions + file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] + stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] + segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] + image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, room_name: _Optional[str] = ..., layout: _Optional[str] = ..., audio_only: bool = ..., audio_mixing: _Optional[_Union[AudioMixing, str]] = ..., video_only: bool = ..., custom_base_url: _Optional[str] = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class WebEgressRequest(_message.Message): + __slots__ = ("url", "audio_only", "video_only", "await_start_signal", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs", "webhooks") + URL_FIELD_NUMBER: _ClassVar[int] + AUDIO_ONLY_FIELD_NUMBER: _ClassVar[int] + VIDEO_ONLY_FIELD_NUMBER: _ClassVar[int] + AWAIT_START_SIGNAL_FIELD_NUMBER: _ClassVar[int] + FILE_FIELD_NUMBER: _ClassVar[int] + STREAM_FIELD_NUMBER: _ClassVar[int] + SEGMENTS_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + url: str + audio_only: bool + video_only: bool + await_start_signal: bool + file: EncodedFileOutput + stream: StreamOutput + segments: SegmentedFileOutput + preset: EncodingOptionsPreset + advanced: EncodingOptions + file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] + stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] + segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] + image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, url: _Optional[str] = ..., audio_only: bool = ..., video_only: bool = ..., await_start_signal: bool = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class ParticipantEgressRequest(_message.Message): + __slots__ = ("room_name", "identity", "screen_share", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs", "webhooks") + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + IDENTITY_FIELD_NUMBER: _ClassVar[int] + SCREEN_SHARE_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + room_name: str + identity: str + screen_share: bool + preset: EncodingOptionsPreset + advanced: EncodingOptions + file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] + stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] + segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] + image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, room_name: _Optional[str] = ..., identity: _Optional[str] = ..., screen_share: bool = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class TrackCompositeEgressRequest(_message.Message): + __slots__ = ("room_name", "audio_track_id", "video_track_id", "file", "stream", "segments", "preset", "advanced", "file_outputs", "stream_outputs", "segment_outputs", "image_outputs", "webhooks") + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + AUDIO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] + VIDEO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] + FILE_FIELD_NUMBER: _ClassVar[int] + STREAM_FIELD_NUMBER: _ClassVar[int] + SEGMENTS_FIELD_NUMBER: _ClassVar[int] + PRESET_FIELD_NUMBER: _ClassVar[int] + ADVANCED_FIELD_NUMBER: _ClassVar[int] + FILE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + STREAM_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + SEGMENT_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + IMAGE_OUTPUTS_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + room_name: str + audio_track_id: str + video_track_id: str + file: EncodedFileOutput + stream: StreamOutput + segments: SegmentedFileOutput + preset: EncodingOptionsPreset + advanced: EncodingOptions + file_outputs: _containers.RepeatedCompositeFieldContainer[EncodedFileOutput] + stream_outputs: _containers.RepeatedCompositeFieldContainer[StreamOutput] + segment_outputs: _containers.RepeatedCompositeFieldContainer[SegmentedFileOutput] + image_outputs: _containers.RepeatedCompositeFieldContainer[ImageOutput] + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, room_name: _Optional[str] = ..., audio_track_id: _Optional[str] = ..., video_track_id: _Optional[str] = ..., file: _Optional[_Union[EncodedFileOutput, _Mapping]] = ..., stream: _Optional[_Union[StreamOutput, _Mapping]] = ..., segments: _Optional[_Union[SegmentedFileOutput, _Mapping]] = ..., preset: _Optional[_Union[EncodingOptionsPreset, str]] = ..., advanced: _Optional[_Union[EncodingOptions, _Mapping]] = ..., file_outputs: _Optional[_Iterable[_Union[EncodedFileOutput, _Mapping]]] = ..., stream_outputs: _Optional[_Iterable[_Union[StreamOutput, _Mapping]]] = ..., segment_outputs: _Optional[_Iterable[_Union[SegmentedFileOutput, _Mapping]]] = ..., image_outputs: _Optional[_Iterable[_Union[ImageOutput, _Mapping]]] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class TrackEgressRequest(_message.Message): + __slots__ = ("room_name", "track_id", "file", "websocket_url", "webhooks") + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + TRACK_ID_FIELD_NUMBER: _ClassVar[int] + FILE_FIELD_NUMBER: _ClassVar[int] + WEBSOCKET_URL_FIELD_NUMBER: _ClassVar[int] + WEBHOOKS_FIELD_NUMBER: _ClassVar[int] + room_name: str + track_id: str + file: DirectFileOutput + websocket_url: str + webhooks: _containers.RepeatedCompositeFieldContainer[_models.WebhookConfig] + def __init__(self, room_name: _Optional[str] = ..., track_id: _Optional[str] = ..., file: _Optional[_Union[DirectFileOutput, _Mapping]] = ..., websocket_url: _Optional[str] = ..., webhooks: _Optional[_Iterable[_Union[_models.WebhookConfig, _Mapping]]] = ...) -> None: ... + +class DirectFileOutput(_message.Message): + __slots__ = ("filepath", "disable_manifest", "s3", "gcp", "azure", "aliOSS") + FILEPATH_FIELD_NUMBER: _ClassVar[int] + DISABLE_MANIFEST_FIELD_NUMBER: _ClassVar[int] + S3_FIELD_NUMBER: _ClassVar[int] + GCP_FIELD_NUMBER: _ClassVar[int] + AZURE_FIELD_NUMBER: _ClassVar[int] + ALIOSS_FIELD_NUMBER: _ClassVar[int] + filepath: str + disable_manifest: bool + s3: S3Upload + gcp: GCPUpload + azure: AzureBlobUpload + aliOSS: AliOSSUpload + def __init__(self, filepath: _Optional[str] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... + +class EncodedFileOutput(_message.Message): + __slots__ = ("file_type", "filepath", "disable_manifest", "s3", "gcp", "azure", "aliOSS") + FILE_TYPE_FIELD_NUMBER: _ClassVar[int] + FILEPATH_FIELD_NUMBER: _ClassVar[int] + DISABLE_MANIFEST_FIELD_NUMBER: _ClassVar[int] + S3_FIELD_NUMBER: _ClassVar[int] + GCP_FIELD_NUMBER: _ClassVar[int] + AZURE_FIELD_NUMBER: _ClassVar[int] + ALIOSS_FIELD_NUMBER: _ClassVar[int] + file_type: EncodedFileType + filepath: str + disable_manifest: bool + s3: S3Upload + gcp: GCPUpload + azure: AzureBlobUpload + aliOSS: AliOSSUpload + def __init__(self, file_type: _Optional[_Union[EncodedFileType, str]] = ..., filepath: _Optional[str] = ..., disable_manifest: bool = ..., s3: _Optional[_Union[S3Upload, _Mapping]] = ..., gcp: _Optional[_Union[GCPUpload, _Mapping]] = ..., azure: _Optional[_Union[AzureBlobUpload, _Mapping]] = ..., aliOSS: _Optional[_Union[AliOSSUpload, _Mapping]] = ...) -> None: ... + +class UpdateLayoutRequest(_message.Message): + __slots__ = ("egress_id", "layout") + EGRESS_ID_FIELD_NUMBER: _ClassVar[int] + LAYOUT_FIELD_NUMBER: _ClassVar[int] + egress_id: str + layout: str + def __init__(self, egress_id: _Optional[str] = ..., layout: _Optional[str] = ...) -> None: ... + +class UpdateStreamRequest(_message.Message): + __slots__ = ("egress_id", "add_output_urls", "remove_output_urls") + EGRESS_ID_FIELD_NUMBER: _ClassVar[int] + ADD_OUTPUT_URLS_FIELD_NUMBER: _ClassVar[int] + REMOVE_OUTPUT_URLS_FIELD_NUMBER: _ClassVar[int] + egress_id: str + add_output_urls: _containers.RepeatedScalarFieldContainer[str] + remove_output_urls: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, egress_id: _Optional[str] = ..., add_output_urls: _Optional[_Iterable[str]] = ..., remove_output_urls: _Optional[_Iterable[str]] = ...) -> None: ... + +class StreamInfoList(_message.Message): + __slots__ = ("info",) + INFO_FIELD_NUMBER: _ClassVar[int] + info: _containers.RepeatedCompositeFieldContainer[StreamInfo] + def __init__(self, info: _Optional[_Iterable[_Union[StreamInfo, _Mapping]]] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/ingress.py b/livekit-protocol/livekit/protocol/ingress.py index 7e29ad86..2dc5f2e3 100644 --- a/livekit-protocol/livekit/protocol/ingress.py +++ b/livekit-protocol/livekit/protocol/ingress.py @@ -1,68 +1,103 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_ingress.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_ingress.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from . import models as _models_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15livekit_ingress.proto\x12\x07livekit\x1a\x14livekit_models.proto\"\xf7\x02\n\x14\x43reateIngressRequest\x12)\n\ninput_type\x18\x01 \x01(\x0e\x32\x15.livekit.IngressInput\x12\x0b\n\x03url\x18\t \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x18\n\x10participant_name\x18\x05 \x01(\t\x12\x1c\n\x14participant_metadata\x18\n \x01(\t\x12\x1e\n\x12\x62ypass_transcoding\x18\x08 \x01(\x08\x42\x02\x18\x01\x12\x1f\n\x12\x65nable_transcoding\x18\x0b \x01(\x08H\x00\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptionsB\x15\n\x13_enable_transcoding\"\xcd\x01\n\x13IngressAudioOptions\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0e\x32\x14.livekit.TrackSource\x12\x35\n\x06preset\x18\x03 \x01(\x0e\x32#.livekit.IngressAudioEncodingPresetH\x00\x12\x37\n\x07options\x18\x04 \x01(\x0b\x32$.livekit.IngressAudioEncodingOptionsH\x00\x42\x12\n\x10\x65ncoding_options\"\xcd\x01\n\x13IngressVideoOptions\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0e\x32\x14.livekit.TrackSource\x12\x35\n\x06preset\x18\x03 \x01(\x0e\x32#.livekit.IngressVideoEncodingPresetH\x00\x12\x37\n\x07options\x18\x04 \x01(\x0b\x32$.livekit.IngressVideoEncodingOptionsH\x00\x42\x12\n\x10\x65ncoding_options\"\x7f\n\x1bIngressAudioEncodingOptions\x12(\n\x0b\x61udio_codec\x18\x01 \x01(\x0e\x32\x13.livekit.AudioCodec\x12\x0f\n\x07\x62itrate\x18\x02 \x01(\r\x12\x13\n\x0b\x64isable_dtx\x18\x03 \x01(\x08\x12\x10\n\x08\x63hannels\x18\x04 \x01(\r\"\x80\x01\n\x1bIngressVideoEncodingOptions\x12(\n\x0bvideo_codec\x18\x01 \x01(\x0e\x32\x13.livekit.VideoCodec\x12\x12\n\nframe_rate\x18\x02 \x01(\x01\x12#\n\x06layers\x18\x03 \x03(\x0b\x32\x13.livekit.VideoLayer\"\xce\x03\n\x0bIngressInfo\x12\x12\n\ningress_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\nstream_key\x18\x03 \x01(\t\x12\x0b\n\x03url\x18\x04 \x01(\t\x12)\n\ninput_type\x18\x05 \x01(\x0e\x32\x15.livekit.IngressInput\x12\x1e\n\x12\x62ypass_transcoding\x18\r \x01(\x08\x42\x02\x18\x01\x12\x1f\n\x12\x65nable_transcoding\x18\x0f \x01(\x08H\x00\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptions\x12\x11\n\troom_name\x18\x08 \x01(\t\x12\x1c\n\x14participant_identity\x18\t \x01(\t\x12\x18\n\x10participant_name\x18\n \x01(\t\x12\x1c\n\x14participant_metadata\x18\x0e \x01(\t\x12\x10\n\x08reusable\x18\x0b \x01(\x08\x12$\n\x05state\x18\x0c \x01(\x0b\x32\x15.livekit.IngressStateB\x15\n\x13_enable_transcoding\"\x9e\x03\n\x0cIngressState\x12,\n\x06status\x18\x01 \x01(\x0e\x32\x1c.livekit.IngressState.Status\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12\'\n\x05video\x18\x03 \x01(\x0b\x32\x18.livekit.InputVideoState\x12\'\n\x05\x61udio\x18\x04 \x01(\x0b\x32\x18.livekit.InputAudioState\x12\x0f\n\x07room_id\x18\x05 \x01(\t\x12\x12\n\nstarted_at\x18\x07 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x08 \x01(\x03\x12\x12\n\nupdated_at\x18\n \x01(\x03\x12\x13\n\x0bresource_id\x18\t \x01(\t\x12\"\n\x06tracks\x18\x06 \x03(\x0b\x32\x12.livekit.TrackInfo\"{\n\x06Status\x12\x15\n\x11\x45NDPOINT_INACTIVE\x10\x00\x12\x16\n\x12\x45NDPOINT_BUFFERING\x10\x01\x12\x17\n\x13\x45NDPOINT_PUBLISHING\x10\x02\x12\x12\n\x0e\x45NDPOINT_ERROR\x10\x03\x12\x15\n\x11\x45NDPOINT_COMPLETE\x10\x04\"o\n\x0fInputVideoState\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x17\n\x0f\x61verage_bitrate\x18\x02 \x01(\r\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12\x11\n\tframerate\x18\x05 \x01(\x01\"d\n\x0fInputAudioState\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x17\n\x0f\x61verage_bitrate\x18\x02 \x01(\r\x12\x10\n\x08\x63hannels\x18\x03 \x01(\r\x12\x13\n\x0bsample_rate\x18\x04 \x01(\r\"\xef\x02\n\x14UpdateIngressRequest\x12\x12\n\ningress_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x18\n\x10participant_name\x18\x05 \x01(\t\x12\x1c\n\x14participant_metadata\x18\t \x01(\t\x12#\n\x12\x62ypass_transcoding\x18\x08 \x01(\x08\x42\x02\x18\x01H\x00\x88\x01\x01\x12\x1f\n\x12\x65nable_transcoding\x18\n \x01(\x08H\x01\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptionsB\x15\n\x13_bypass_transcodingB\x15\n\x13_enable_transcoding\";\n\x12ListIngressRequest\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x12\n\ningress_id\x18\x02 \x01(\t\":\n\x13ListIngressResponse\x12#\n\x05items\x18\x01 \x03(\x0b\x32\x14.livekit.IngressInfo\"*\n\x14\x44\x65leteIngressRequest\x12\x12\n\ningress_id\x18\x01 \x01(\t*=\n\x0cIngressInput\x12\x0e\n\nRTMP_INPUT\x10\x00\x12\x0e\n\nWHIP_INPUT\x10\x01\x12\r\n\tURL_INPUT\x10\x02*I\n\x1aIngressAudioEncodingPreset\x12\x16\n\x12OPUS_STEREO_96KBPS\x10\x00\x12\x13\n\x0fOPUS_MONO_64KBS\x10\x01*\x84\x03\n\x1aIngressVideoEncodingPreset\x12\x1c\n\x18H264_720P_30FPS_3_LAYERS\x10\x00\x12\x1d\n\x19H264_1080P_30FPS_3_LAYERS\x10\x01\x12\x1c\n\x18H264_540P_25FPS_2_LAYERS\x10\x02\x12\x1b\n\x17H264_720P_30FPS_1_LAYER\x10\x03\x12\x1c\n\x18H264_1080P_30FPS_1_LAYER\x10\x04\x12(\n$H264_720P_30FPS_3_LAYERS_HIGH_MOTION\x10\x05\x12)\n%H264_1080P_30FPS_3_LAYERS_HIGH_MOTION\x10\x06\x12(\n$H264_540P_25FPS_2_LAYERS_HIGH_MOTION\x10\x07\x12\'\n#H264_720P_30FPS_1_LAYER_HIGH_MOTION\x10\x08\x12(\n$H264_1080P_30FPS_1_LAYER_HIGH_MOTION\x10\t2\xa5\x02\n\x07Ingress\x12\x44\n\rCreateIngress\x12\x1d.livekit.CreateIngressRequest\x1a\x14.livekit.IngressInfo\x12\x44\n\rUpdateIngress\x12\x1d.livekit.UpdateIngressRequest\x1a\x14.livekit.IngressInfo\x12H\n\x0bListIngress\x12\x1b.livekit.ListIngressRequest\x1a\x1c.livekit.ListIngressResponse\x12\x44\n\rDeleteIngress\x12\x1d.livekit.DeleteIngressRequest\x1a\x14.livekit.IngressInfoBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15livekit_ingress.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14logger/options.proto\"\xc4\x03\n\x14\x43reateIngressRequest\x12)\n\ninput_type\x18\x01 \x01(\x0e\x32\x15.livekit.IngressInput\x12\x0b\n\x03url\x18\t \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x1d\n\x10participant_name\x18\x05 \x01(\tB\x03\xc0P\x01\x12\x42\n\x14participant_metadata\x18\n \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x1e\n\x12\x62ypass_transcoding\x18\x08 \x01(\x08\x42\x02\x18\x01\x12\x1f\n\x12\x65nable_transcoding\x18\x0b \x01(\x08H\x00\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptions\x12\x14\n\x07\x65nabled\x18\x0c \x01(\x08H\x01\x88\x01\x01\x42\x15\n\x13_enable_transcodingB\n\n\x08_enabled\"\xcd\x01\n\x13IngressAudioOptions\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0e\x32\x14.livekit.TrackSource\x12\x35\n\x06preset\x18\x03 \x01(\x0e\x32#.livekit.IngressAudioEncodingPresetH\x00\x12\x37\n\x07options\x18\x04 \x01(\x0b\x32$.livekit.IngressAudioEncodingOptionsH\x00\x42\x12\n\x10\x65ncoding_options\"\xcd\x01\n\x13IngressVideoOptions\x12\x0c\n\x04name\x18\x01 \x01(\t\x12$\n\x06source\x18\x02 \x01(\x0e\x32\x14.livekit.TrackSource\x12\x35\n\x06preset\x18\x03 \x01(\x0e\x32#.livekit.IngressVideoEncodingPresetH\x00\x12\x37\n\x07options\x18\x04 \x01(\x0b\x32$.livekit.IngressVideoEncodingOptionsH\x00\x42\x12\n\x10\x65ncoding_options\"\x7f\n\x1bIngressAudioEncodingOptions\x12(\n\x0b\x61udio_codec\x18\x01 \x01(\x0e\x32\x13.livekit.AudioCodec\x12\x0f\n\x07\x62itrate\x18\x02 \x01(\r\x12\x13\n\x0b\x64isable_dtx\x18\x03 \x01(\x08\x12\x10\n\x08\x63hannels\x18\x04 \x01(\r\"\x80\x01\n\x1bIngressVideoEncodingOptions\x12(\n\x0bvideo_codec\x18\x01 \x01(\x0e\x32\x13.livekit.VideoCodec\x12\x12\n\nframe_rate\x18\x02 \x01(\x01\x12#\n\x06layers\x18\x03 \x03(\x0b\x32\x13.livekit.VideoLayer\"\xa9\x04\n\x0bIngressInfo\x12 \n\ningress_id\x18\x01 \x01(\tB\x0c\xbaP\tingressID\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\nstream_key\x18\x03 \x01(\t\x12\x0b\n\x03url\x18\x04 \x01(\t\x12)\n\ninput_type\x18\x05 \x01(\x0e\x32\x15.livekit.IngressInput\x12\x1e\n\x12\x62ypass_transcoding\x18\r \x01(\x08\x42\x02\x18\x01\x12\x1f\n\x12\x65nable_transcoding\x18\x0f \x01(\x08H\x00\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptions\x12\x11\n\troom_name\x18\x08 \x01(\t\x12\x1c\n\x14participant_identity\x18\t \x01(\t\x12\x1d\n\x10participant_name\x18\n \x01(\tB\x03\xc0P\x01\x12\x42\n\x14participant_metadata\x18\x0e \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x10\n\x08reusable\x18\x0b \x01(\x08\x12$\n\x05state\x18\x0c \x01(\x0b\x32\x15.livekit.IngressState\x12\x14\n\x07\x65nabled\x18\x10 \x01(\x08H\x01\x88\x01\x01\x42\x15\n\x13_enable_transcodingB\n\n\x08_enabled\"\xb8\x03\n\x0cIngressState\x12,\n\x06status\x18\x01 \x01(\x0e\x32\x1c.livekit.IngressState.Status\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12\'\n\x05video\x18\x03 \x01(\x0b\x32\x18.livekit.InputVideoState\x12\'\n\x05\x61udio\x18\x04 \x01(\x0b\x32\x18.livekit.InputAudioState\x12\x1a\n\x07room_id\x18\x05 \x01(\tB\t\xbaP\x06roomID\x12\x12\n\nstarted_at\x18\x07 \x01(\x03\x12\x10\n\x08\x65nded_at\x18\x08 \x01(\x03\x12\x12\n\nupdated_at\x18\n \x01(\x03\x12\"\n\x0bresource_id\x18\t \x01(\tB\r\xbaP\nresourceID\x12\"\n\x06tracks\x18\x06 \x03(\x0b\x32\x12.livekit.TrackInfo\"{\n\x06Status\x12\x15\n\x11\x45NDPOINT_INACTIVE\x10\x00\x12\x16\n\x12\x45NDPOINT_BUFFERING\x10\x01\x12\x17\n\x13\x45NDPOINT_PUBLISHING\x10\x02\x12\x12\n\x0e\x45NDPOINT_ERROR\x10\x03\x12\x15\n\x11\x45NDPOINT_COMPLETE\x10\x04\"o\n\x0fInputVideoState\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x17\n\x0f\x61verage_bitrate\x18\x02 \x01(\r\x12\r\n\x05width\x18\x03 \x01(\r\x12\x0e\n\x06height\x18\x04 \x01(\r\x12\x11\n\tframerate\x18\x05 \x01(\x01\"d\n\x0fInputAudioState\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x17\n\x0f\x61verage_bitrate\x18\x02 \x01(\r\x12\x10\n\x08\x63hannels\x18\x03 \x01(\r\x12\x13\n\x0bsample_rate\x18\x04 \x01(\r\"\xca\x03\n\x14UpdateIngressRequest\x12 \n\ningress_id\x18\x01 \x01(\tB\x0c\xbaP\tingressID\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x1d\n\x10participant_name\x18\x05 \x01(\tB\x03\xc0P\x01\x12\x42\n\x14participant_metadata\x18\t \x01(\tB$\xb2P\x1e\xc0P\x01\x12#\n\x12\x62ypass_transcoding\x18\x08 \x01(\x08\x42\x02\x18\x01H\x00\x88\x01\x01\x12\x1f\n\x12\x65nable_transcoding\x18\n \x01(\x08H\x01\x88\x01\x01\x12+\n\x05\x61udio\x18\x06 \x01(\x0b\x32\x1c.livekit.IngressAudioOptions\x12+\n\x05video\x18\x07 \x01(\x0b\x32\x1c.livekit.IngressVideoOptions\x12\x14\n\x07\x65nabled\x18\x0b \x01(\x08H\x02\x88\x01\x01\x42\x15\n\x13_bypass_transcodingB\x15\n\x13_enable_transcodingB\n\n\x08_enabled\"w\n\x12ListIngressRequest\x12,\n\npage_token\x18\x03 \x01(\x0b\x32\x18.livekit.TokenPagination\x12\x11\n\troom_name\x18\x01 \x01(\t\x12 \n\ningress_id\x18\x02 \x01(\tB\x0c\xbaP\tingressID\"m\n\x13ListIngressResponse\x12\x31\n\x0fnext_page_token\x18\x02 \x01(\x0b\x32\x18.livekit.TokenPagination\x12#\n\x05items\x18\x01 \x03(\x0b\x32\x14.livekit.IngressInfo\"8\n\x14\x44\x65leteIngressRequest\x12 \n\ningress_id\x18\x01 \x01(\tB\x0c\xbaP\tingressID*=\n\x0cIngressInput\x12\x0e\n\nRTMP_INPUT\x10\x00\x12\x0e\n\nWHIP_INPUT\x10\x01\x12\r\n\tURL_INPUT\x10\x02*I\n\x1aIngressAudioEncodingPreset\x12\x16\n\x12OPUS_STEREO_96KBPS\x10\x00\x12\x13\n\x0fOPUS_MONO_64KBS\x10\x01*\x84\x03\n\x1aIngressVideoEncodingPreset\x12\x1c\n\x18H264_720P_30FPS_3_LAYERS\x10\x00\x12\x1d\n\x19H264_1080P_30FPS_3_LAYERS\x10\x01\x12\x1c\n\x18H264_540P_25FPS_2_LAYERS\x10\x02\x12\x1b\n\x17H264_720P_30FPS_1_LAYER\x10\x03\x12\x1c\n\x18H264_1080P_30FPS_1_LAYER\x10\x04\x12(\n$H264_720P_30FPS_3_LAYERS_HIGH_MOTION\x10\x05\x12)\n%H264_1080P_30FPS_3_LAYERS_HIGH_MOTION\x10\x06\x12(\n$H264_540P_25FPS_2_LAYERS_HIGH_MOTION\x10\x07\x12\'\n#H264_720P_30FPS_1_LAYER_HIGH_MOTION\x10\x08\x12(\n$H264_1080P_30FPS_1_LAYER_HIGH_MOTION\x10\t2\xa5\x02\n\x07Ingress\x12\x44\n\rCreateIngress\x12\x1d.livekit.CreateIngressRequest\x1a\x14.livekit.IngressInfo\x12\x44\n\rUpdateIngress\x12\x1d.livekit.UpdateIngressRequest\x1a\x14.livekit.IngressInfo\x12H\n\x0bListIngress\x12\x1b.livekit.ListIngressRequest\x1a\x1c.livekit.ListIngressResponse\x12\x44\n\rDeleteIngress\x12\x1d.livekit.DeleteIngressRequest\x1a\x14.livekit.IngressInfoBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'ingress', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_CREATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._options = None + _globals['_CREATEINGRESSREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_CREATEINGRESSREQUEST'].fields_by_name['participant_name']._serialized_options = b'\300P\001' + _globals['_CREATEINGRESSREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_CREATEINGRESSREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._loaded_options = None _globals['_CREATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._serialized_options = b'\030\001' - _globals['_INGRESSINFO'].fields_by_name['bypass_transcoding']._options = None + _globals['_INGRESSINFO'].fields_by_name['ingress_id']._loaded_options = None + _globals['_INGRESSINFO'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_INGRESSINFO'].fields_by_name['bypass_transcoding']._loaded_options = None _globals['_INGRESSINFO'].fields_by_name['bypass_transcoding']._serialized_options = b'\030\001' - _globals['_UPDATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._options = None + _globals['_INGRESSINFO'].fields_by_name['participant_name']._loaded_options = None + _globals['_INGRESSINFO'].fields_by_name['participant_name']._serialized_options = b'\300P\001' + _globals['_INGRESSINFO'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_INGRESSINFO'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_INGRESSSTATE'].fields_by_name['room_id']._loaded_options = None + _globals['_INGRESSSTATE'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_INGRESSSTATE'].fields_by_name['resource_id']._loaded_options = None + _globals['_INGRESSSTATE'].fields_by_name['resource_id']._serialized_options = b'\272P\nresourceID' + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['ingress_id']._loaded_options = None + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['participant_name']._serialized_options = b'\300P\001' + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._loaded_options = None _globals['_UPDATEINGRESSREQUEST'].fields_by_name['bypass_transcoding']._serialized_options = b'\030\001' - _globals['_INGRESSINPUT']._serialized_start=2742 - _globals['_INGRESSINPUT']._serialized_end=2803 - _globals['_INGRESSAUDIOENCODINGPRESET']._serialized_start=2805 - _globals['_INGRESSAUDIOENCODINGPRESET']._serialized_end=2878 - _globals['_INGRESSVIDEOENCODINGPRESET']._serialized_start=2881 - _globals['_INGRESSVIDEOENCODINGPRESET']._serialized_end=3269 - _globals['_CREATEINGRESSREQUEST']._serialized_start=57 - _globals['_CREATEINGRESSREQUEST']._serialized_end=432 - _globals['_INGRESSAUDIOOPTIONS']._serialized_start=435 - _globals['_INGRESSAUDIOOPTIONS']._serialized_end=640 - _globals['_INGRESSVIDEOOPTIONS']._serialized_start=643 - _globals['_INGRESSVIDEOOPTIONS']._serialized_end=848 - _globals['_INGRESSAUDIOENCODINGOPTIONS']._serialized_start=850 - _globals['_INGRESSAUDIOENCODINGOPTIONS']._serialized_end=977 - _globals['_INGRESSVIDEOENCODINGOPTIONS']._serialized_start=980 - _globals['_INGRESSVIDEOENCODINGOPTIONS']._serialized_end=1108 - _globals['_INGRESSINFO']._serialized_start=1111 - _globals['_INGRESSINFO']._serialized_end=1573 - _globals['_INGRESSSTATE']._serialized_start=1576 - _globals['_INGRESSSTATE']._serialized_end=1990 - _globals['_INGRESSSTATE_STATUS']._serialized_start=1867 - _globals['_INGRESSSTATE_STATUS']._serialized_end=1990 - _globals['_INPUTVIDEOSTATE']._serialized_start=1992 - _globals['_INPUTVIDEOSTATE']._serialized_end=2103 - _globals['_INPUTAUDIOSTATE']._serialized_start=2105 - _globals['_INPUTAUDIOSTATE']._serialized_end=2205 - _globals['_UPDATEINGRESSREQUEST']._serialized_start=2208 - _globals['_UPDATEINGRESSREQUEST']._serialized_end=2575 - _globals['_LISTINGRESSREQUEST']._serialized_start=2577 - _globals['_LISTINGRESSREQUEST']._serialized_end=2636 - _globals['_LISTINGRESSRESPONSE']._serialized_start=2638 - _globals['_LISTINGRESSRESPONSE']._serialized_end=2696 - _globals['_DELETEINGRESSREQUEST']._serialized_start=2698 - _globals['_DELETEINGRESSREQUEST']._serialized_end=2740 - _globals['_INGRESS']._serialized_start=3272 - _globals['_INGRESS']._serialized_end=3565 + _globals['_LISTINGRESSREQUEST'].fields_by_name['ingress_id']._loaded_options = None + _globals['_LISTINGRESSREQUEST'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_DELETEINGRESSREQUEST'].fields_by_name['ingress_id']._loaded_options = None + _globals['_DELETEINGRESSREQUEST'].fields_by_name['ingress_id']._serialized_options = b'\272P\tingressID' + _globals['_INGRESSINPUT']._serialized_start=3174 + _globals['_INGRESSINPUT']._serialized_end=3235 + _globals['_INGRESSAUDIOENCODINGPRESET']._serialized_start=3237 + _globals['_INGRESSAUDIOENCODINGPRESET']._serialized_end=3310 + _globals['_INGRESSVIDEOENCODINGPRESET']._serialized_start=3313 + _globals['_INGRESSVIDEOENCODINGPRESET']._serialized_end=3701 + _globals['_CREATEINGRESSREQUEST']._serialized_start=79 + _globals['_CREATEINGRESSREQUEST']._serialized_end=531 + _globals['_INGRESSAUDIOOPTIONS']._serialized_start=534 + _globals['_INGRESSAUDIOOPTIONS']._serialized_end=739 + _globals['_INGRESSVIDEOOPTIONS']._serialized_start=742 + _globals['_INGRESSVIDEOOPTIONS']._serialized_end=947 + _globals['_INGRESSAUDIOENCODINGOPTIONS']._serialized_start=949 + _globals['_INGRESSAUDIOENCODINGOPTIONS']._serialized_end=1076 + _globals['_INGRESSVIDEOENCODINGOPTIONS']._serialized_start=1079 + _globals['_INGRESSVIDEOENCODINGOPTIONS']._serialized_end=1207 + _globals['_INGRESSINFO']._serialized_start=1210 + _globals['_INGRESSINFO']._serialized_end=1763 + _globals['_INGRESSSTATE']._serialized_start=1766 + _globals['_INGRESSSTATE']._serialized_end=2206 + _globals['_INGRESSSTATE_STATUS']._serialized_start=2083 + _globals['_INGRESSSTATE_STATUS']._serialized_end=2206 + _globals['_INPUTVIDEOSTATE']._serialized_start=2208 + _globals['_INPUTVIDEOSTATE']._serialized_end=2319 + _globals['_INPUTAUDIOSTATE']._serialized_start=2321 + _globals['_INPUTAUDIOSTATE']._serialized_end=2421 + _globals['_UPDATEINGRESSREQUEST']._serialized_start=2424 + _globals['_UPDATEINGRESSREQUEST']._serialized_end=2882 + _globals['_LISTINGRESSREQUEST']._serialized_start=2884 + _globals['_LISTINGRESSREQUEST']._serialized_end=3003 + _globals['_LISTINGRESSRESPONSE']._serialized_start=3005 + _globals['_LISTINGRESSRESPONSE']._serialized_end=3114 + _globals['_DELETEINGRESSREQUEST']._serialized_start=3116 + _globals['_DELETEINGRESSREQUEST']._serialized_end=3172 + _globals['_INGRESS']._serialized_start=3704 + _globals['_INGRESS']._serialized_end=3997 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/ingress.pyi b/livekit-protocol/livekit/protocol/ingress.pyi index fb9f8273..d214cba8 100644 --- a/livekit-protocol/livekit/protocol/ingress.pyi +++ b/livekit-protocol/livekit/protocol/ingress.pyi @@ -1,4 +1,5 @@ from . import models as _models +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -47,7 +48,7 @@ H264_720P_30FPS_1_LAYER_HIGH_MOTION: IngressVideoEncodingPreset H264_1080P_30FPS_1_LAYER_HIGH_MOTION: IngressVideoEncodingPreset class CreateIngressRequest(_message.Message): - __slots__ = ("input_type", "url", "name", "room_name", "participant_identity", "participant_name", "participant_metadata", "bypass_transcoding", "enable_transcoding", "audio", "video") + __slots__ = ("input_type", "url", "name", "room_name", "participant_identity", "participant_name", "participant_metadata", "bypass_transcoding", "enable_transcoding", "audio", "video", "enabled") INPUT_TYPE_FIELD_NUMBER: _ClassVar[int] URL_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] @@ -59,6 +60,7 @@ class CreateIngressRequest(_message.Message): ENABLE_TRANSCODING_FIELD_NUMBER: _ClassVar[int] AUDIO_FIELD_NUMBER: _ClassVar[int] VIDEO_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] input_type: IngressInput url: str name: str @@ -70,7 +72,8 @@ class CreateIngressRequest(_message.Message): enable_transcoding: bool audio: IngressAudioOptions video: IngressVideoOptions - def __init__(self, input_type: _Optional[_Union[IngressInput, str]] = ..., url: _Optional[str] = ..., name: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ...) -> None: ... + enabled: bool + def __init__(self, input_type: _Optional[_Union[IngressInput, str]] = ..., url: _Optional[str] = ..., name: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ..., enabled: bool = ...) -> None: ... class IngressAudioOptions(_message.Message): __slots__ = ("name", "source", "preset", "options") @@ -119,7 +122,7 @@ class IngressVideoEncodingOptions(_message.Message): def __init__(self, video_codec: _Optional[_Union[_models.VideoCodec, str]] = ..., frame_rate: _Optional[float] = ..., layers: _Optional[_Iterable[_Union[_models.VideoLayer, _Mapping]]] = ...) -> None: ... class IngressInfo(_message.Message): - __slots__ = ("ingress_id", "name", "stream_key", "url", "input_type", "bypass_transcoding", "enable_transcoding", "audio", "video", "room_name", "participant_identity", "participant_name", "participant_metadata", "reusable", "state") + __slots__ = ("ingress_id", "name", "stream_key", "url", "input_type", "bypass_transcoding", "enable_transcoding", "audio", "video", "room_name", "participant_identity", "participant_name", "participant_metadata", "reusable", "state", "enabled") INGRESS_ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] STREAM_KEY_FIELD_NUMBER: _ClassVar[int] @@ -135,6 +138,7 @@ class IngressInfo(_message.Message): PARTICIPANT_METADATA_FIELD_NUMBER: _ClassVar[int] REUSABLE_FIELD_NUMBER: _ClassVar[int] STATE_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] ingress_id: str name: str stream_key: str @@ -150,7 +154,8 @@ class IngressInfo(_message.Message): participant_metadata: str reusable: bool state: IngressState - def __init__(self, ingress_id: _Optional[str] = ..., name: _Optional[str] = ..., stream_key: _Optional[str] = ..., url: _Optional[str] = ..., input_type: _Optional[_Union[IngressInput, str]] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., reusable: bool = ..., state: _Optional[_Union[IngressState, _Mapping]] = ...) -> None: ... + enabled: bool + def __init__(self, ingress_id: _Optional[str] = ..., name: _Optional[str] = ..., stream_key: _Optional[str] = ..., url: _Optional[str] = ..., input_type: _Optional[_Union[IngressInput, str]] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., reusable: bool = ..., state: _Optional[_Union[IngressState, _Mapping]] = ..., enabled: bool = ...) -> None: ... class IngressState(_message.Message): __slots__ = ("status", "error", "video", "audio", "room_id", "started_at", "ended_at", "updated_at", "resource_id", "tracks") @@ -215,7 +220,7 @@ class InputAudioState(_message.Message): def __init__(self, mime_type: _Optional[str] = ..., average_bitrate: _Optional[int] = ..., channels: _Optional[int] = ..., sample_rate: _Optional[int] = ...) -> None: ... class UpdateIngressRequest(_message.Message): - __slots__ = ("ingress_id", "name", "room_name", "participant_identity", "participant_name", "participant_metadata", "bypass_transcoding", "enable_transcoding", "audio", "video") + __slots__ = ("ingress_id", "name", "room_name", "participant_identity", "participant_name", "participant_metadata", "bypass_transcoding", "enable_transcoding", "audio", "video", "enabled") INGRESS_ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] ROOM_NAME_FIELD_NUMBER: _ClassVar[int] @@ -226,6 +231,7 @@ class UpdateIngressRequest(_message.Message): ENABLE_TRANSCODING_FIELD_NUMBER: _ClassVar[int] AUDIO_FIELD_NUMBER: _ClassVar[int] VIDEO_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] ingress_id: str name: str room_name: str @@ -236,21 +242,26 @@ class UpdateIngressRequest(_message.Message): enable_transcoding: bool audio: IngressAudioOptions video: IngressVideoOptions - def __init__(self, ingress_id: _Optional[str] = ..., name: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ...) -> None: ... + enabled: bool + def __init__(self, ingress_id: _Optional[str] = ..., name: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., bypass_transcoding: bool = ..., enable_transcoding: bool = ..., audio: _Optional[_Union[IngressAudioOptions, _Mapping]] = ..., video: _Optional[_Union[IngressVideoOptions, _Mapping]] = ..., enabled: bool = ...) -> None: ... class ListIngressRequest(_message.Message): - __slots__ = ("room_name", "ingress_id") + __slots__ = ("page_token", "room_name", "ingress_id") + PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] ROOM_NAME_FIELD_NUMBER: _ClassVar[int] INGRESS_ID_FIELD_NUMBER: _ClassVar[int] + page_token: _models.TokenPagination room_name: str ingress_id: str - def __init__(self, room_name: _Optional[str] = ..., ingress_id: _Optional[str] = ...) -> None: ... + def __init__(self, page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ..., room_name: _Optional[str] = ..., ingress_id: _Optional[str] = ...) -> None: ... class ListIngressResponse(_message.Message): - __slots__ = ("items",) + __slots__ = ("next_page_token", "items") + NEXT_PAGE_TOKEN_FIELD_NUMBER: _ClassVar[int] ITEMS_FIELD_NUMBER: _ClassVar[int] + next_page_token: _models.TokenPagination items: _containers.RepeatedCompositeFieldContainer[IngressInfo] - def __init__(self, items: _Optional[_Iterable[_Union[IngressInfo, _Mapping]]] = ...) -> None: ... + def __init__(self, next_page_token: _Optional[_Union[_models.TokenPagination, _Mapping]] = ..., items: _Optional[_Iterable[_Union[IngressInfo, _Mapping]]] = ...) -> None: ... class DeleteIngressRequest(_message.Message): __slots__ = ("ingress_id",) diff --git a/livekit-protocol/livekit/protocol/logger_pb/__init__.py b/livekit-protocol/livekit/protocol/logger_pb/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/livekit-protocol/livekit/protocol/logger_pb/options.py b/livekit-protocol/livekit/protocol/logger_pb/options.py new file mode 100644 index 00000000..f005c630 --- /dev/null +++ b/livekit-protocol/livekit/protocol/logger_pb/options.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: logger/options.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'logger/options.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14logger/options.proto\x12\x06logger\x1a google/protobuf/descriptor.proto*W\n\x0bSensitivity\x12\x1b\n\x17SENSITIVITY_UNSPECIFIED\x10\x00\x12\x13\n\x0fSENSITIVITY_PII\x10\x01\x12\x16\n\x12SENSITIVITY_SECRET\x10\x02:5\n\rredact_format\x12\x1d.google.protobuf.FieldOptions\x18\x86\n \x01(\t:,\n\x04name\x12\x1d.google.protobuf.FieldOptions\x18\x87\n \x01(\t:H\n\x0bsensitivity\x12\x1d.google.protobuf.FieldOptions\x18\x88\n \x01(\x0e\x32\x13.logger.SensitivityBMZ*github.com/livekit/protocol/livekit/logger\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'logger.options_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z*github.com/livekit/protocol/livekit/logger\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_SENSITIVITY']._serialized_start=66 + _globals['_SENSITIVITY']._serialized_end=153 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/logger_pb/options.pyi b/livekit-protocol/livekit/protocol/logger_pb/options.pyi new file mode 100644 index 00000000..8f54a6bf --- /dev/null +++ b/livekit-protocol/livekit/protocol/logger_pb/options.pyi @@ -0,0 +1,21 @@ +from google.protobuf import descriptor_pb2 as _descriptor_pb2 +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from typing import ClassVar as _ClassVar + +DESCRIPTOR: _descriptor.FileDescriptor + +class Sensitivity(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SENSITIVITY_UNSPECIFIED: _ClassVar[Sensitivity] + SENSITIVITY_PII: _ClassVar[Sensitivity] + SENSITIVITY_SECRET: _ClassVar[Sensitivity] +SENSITIVITY_UNSPECIFIED: Sensitivity +SENSITIVITY_PII: Sensitivity +SENSITIVITY_SECRET: Sensitivity +REDACT_FORMAT_FIELD_NUMBER: int +redact_format: _descriptor.FieldDescriptor +NAME_FIELD_NUMBER: int +name: _descriptor.FieldDescriptor +SENSITIVITY_FIELD_NUMBER: int +sensitivity: _descriptor.FieldDescriptor diff --git a/livekit-protocol/livekit/protocol/metrics.py b/livekit-protocol/livekit/protocol/metrics.py new file mode 100644 index 00000000..a1df7f1b --- /dev/null +++ b/livekit-protocol/livekit/protocol/metrics.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_metrics.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_metrics.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15livekit_metrics.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14logger/options.proto\"\xc6\x01\n\x0cMetricsBatch\x12\x14\n\x0ctimestamp_ms\x18\x01 \x01(\x03\x12\x38\n\x14normalized_timestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08str_data\x18\x03 \x03(\t\x12.\n\x0btime_series\x18\x04 \x03(\x0b\x32\x19.livekit.TimeSeriesMetric\x12$\n\x06\x65vents\x18\x05 \x03(\x0b\x32\x14.livekit.EventMetric\"\x87\x01\n\x10TimeSeriesMetric\x12\r\n\x05label\x18\x01 \x01(\r\x12\x1c\n\x14participant_identity\x18\x02 \x01(\r\x12\x11\n\ttrack_sid\x18\x03 \x01(\r\x12&\n\x07samples\x18\x04 \x03(\x0b\x32\x15.livekit.MetricSample\x12\x0b\n\x03rid\x18\x05 \x01(\r\"m\n\x0cMetricSample\x12\x14\n\x0ctimestamp_ms\x18\x01 \x01(\x03\x12\x38\n\x14normalized_timestamp\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\r\n\x05value\x18\x03 \x01(\x02\"\xdc\x02\n\x0b\x45ventMetric\x12\r\n\x05label\x18\x01 \x01(\r\x12\x1c\n\x14participant_identity\x18\x02 \x01(\r\x12\x11\n\ttrack_sid\x18\x03 \x01(\r\x12\x1a\n\x12start_timestamp_ms\x18\x04 \x01(\x03\x12\x1d\n\x10\x65nd_timestamp_ms\x18\x05 \x01(\x03H\x00\x88\x01\x01\x12>\n\x1anormalized_start_timestamp\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x41\n\x18normalized_end_timestamp\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.TimestampH\x01\x88\x01\x01\x12\x10\n\x08metadata\x18\x08 \x01(\t\x12\x0b\n\x03rid\x18\t \x01(\rB\x13\n\x11_end_timestamp_msB\x1b\n\x19_normalized_end_timestamp\"\xf5\x02\n\x16MetricsRecordingHeader\x12\x1a\n\x07room_id\x18\x01 \x01(\tB\t\xbaP\x06roomID\x12\x10\n\x08\x64uration\x18\x03 \x01(\x04\x12.\n\nstart_time\x18\x04 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12@\n\troom_tags\x18\x05 \x03(\x0b\x32-.livekit.MetricsRecordingHeader.RoomTagsEntry\x12\x11\n\troom_name\x18\x06 \x01(\t\x12\x33\n\x0froom_start_time\x18\x07 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0e\n\x06job_id\x18\x08 \x01(\t\x12\x11\n\tsimulated\x18\t \x01(\x08\x12\x19\n\x11redaction_enabled\x18\n \x01(\x08\x1a/\n\rRoomTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01J\x04\x08\x02\x10\x03*\x81\x07\n\x0bMetricLabel\x12\x13\n\x0f\x41GENTS_LLM_TTFT\x10\x00\x12\x13\n\x0f\x41GENTS_STT_TTFT\x10\x01\x12\x13\n\x0f\x41GENTS_TTS_TTFB\x10\x02\x12(\n$CLIENT_VIDEO_SUBSCRIBER_FREEZE_COUNT\x10\x03\x12\x31\n-CLIENT_VIDEO_SUBSCRIBER_TOTAL_FREEZE_DURATION\x10\x04\x12\'\n#CLIENT_VIDEO_SUBSCRIBER_PAUSE_COUNT\x10\x05\x12\x31\n-CLIENT_VIDEO_SUBSCRIBER_TOTAL_PAUSES_DURATION\x10\x06\x12-\n)CLIENT_AUDIO_SUBSCRIBER_CONCEALED_SAMPLES\x10\x07\x12\x34\n0CLIENT_AUDIO_SUBSCRIBER_SILENT_CONCEALED_SAMPLES\x10\x08\x12.\n*CLIENT_AUDIO_SUBSCRIBER_CONCEALMENT_EVENTS\x10\t\x12.\n*CLIENT_AUDIO_SUBSCRIBER_INTERRUPTION_COUNT\x10\n\x12\x37\n3CLIENT_AUDIO_SUBSCRIBER_TOTAL_INTERRUPTION_DURATION\x10\x0b\x12)\n%CLIENT_SUBSCRIBER_JITTER_BUFFER_DELAY\x10\x0c\x12\x31\n-CLIENT_SUBSCRIBER_JITTER_BUFFER_EMITTED_COUNT\x10\r\x12@\n None: ... + +class TimeSeriesMetric(_message.Message): + __slots__ = ("label", "participant_identity", "track_sid", "samples", "rid") + LABEL_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + SAMPLES_FIELD_NUMBER: _ClassVar[int] + RID_FIELD_NUMBER: _ClassVar[int] + label: int + participant_identity: int + track_sid: int + samples: _containers.RepeatedCompositeFieldContainer[MetricSample] + rid: int + def __init__(self, label: _Optional[int] = ..., participant_identity: _Optional[int] = ..., track_sid: _Optional[int] = ..., samples: _Optional[_Iterable[_Union[MetricSample, _Mapping]]] = ..., rid: _Optional[int] = ...) -> None: ... + +class MetricSample(_message.Message): + __slots__ = ("timestamp_ms", "normalized_timestamp", "value") + TIMESTAMP_MS_FIELD_NUMBER: _ClassVar[int] + NORMALIZED_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + timestamp_ms: int + normalized_timestamp: _timestamp_pb2.Timestamp + value: float + def __init__(self, timestamp_ms: _Optional[int] = ..., normalized_timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., value: _Optional[float] = ...) -> None: ... + +class EventMetric(_message.Message): + __slots__ = ("label", "participant_identity", "track_sid", "start_timestamp_ms", "end_timestamp_ms", "normalized_start_timestamp", "normalized_end_timestamp", "metadata", "rid") + LABEL_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + START_TIMESTAMP_MS_FIELD_NUMBER: _ClassVar[int] + END_TIMESTAMP_MS_FIELD_NUMBER: _ClassVar[int] + NORMALIZED_START_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + NORMALIZED_END_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + RID_FIELD_NUMBER: _ClassVar[int] + label: int + participant_identity: int + track_sid: int + start_timestamp_ms: int + end_timestamp_ms: int + normalized_start_timestamp: _timestamp_pb2.Timestamp + normalized_end_timestamp: _timestamp_pb2.Timestamp + metadata: str + rid: int + def __init__(self, label: _Optional[int] = ..., participant_identity: _Optional[int] = ..., track_sid: _Optional[int] = ..., start_timestamp_ms: _Optional[int] = ..., end_timestamp_ms: _Optional[int] = ..., normalized_start_timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., normalized_end_timestamp: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., metadata: _Optional[str] = ..., rid: _Optional[int] = ...) -> None: ... + +class MetricsRecordingHeader(_message.Message): + __slots__ = ("room_id", "duration", "start_time", "room_tags", "room_name", "room_start_time", "job_id", "simulated", "redaction_enabled") + class RoomTagsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + DURATION_FIELD_NUMBER: _ClassVar[int] + START_TIME_FIELD_NUMBER: _ClassVar[int] + ROOM_TAGS_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_START_TIME_FIELD_NUMBER: _ClassVar[int] + JOB_ID_FIELD_NUMBER: _ClassVar[int] + SIMULATED_FIELD_NUMBER: _ClassVar[int] + REDACTION_ENABLED_FIELD_NUMBER: _ClassVar[int] + room_id: str + duration: int + start_time: _timestamp_pb2.Timestamp + room_tags: _containers.ScalarMap[str, str] + room_name: str + room_start_time: _timestamp_pb2.Timestamp + job_id: str + simulated: bool + redaction_enabled: bool + def __init__(self, room_id: _Optional[str] = ..., duration: _Optional[int] = ..., start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., room_tags: _Optional[_Mapping[str, str]] = ..., room_name: _Optional[str] = ..., room_start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., job_id: _Optional[str] = ..., simulated: bool = ..., redaction_enabled: bool = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/models.py b/livekit-protocol/livekit/protocol/models.py index 16037c31..e15bcbb6 100644 --- a/livekit-protocol/livekit/protocol/models.py +++ b/livekit-protocol/livekit/protocol/models.py @@ -1,136 +1,286 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_models.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_models.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from . import metrics as _metrics_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14livekit_models.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\"\xc9\x02\n\x04Room\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x15\n\rempty_timeout\x18\x03 \x01(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\x0e \x01(\r\x12\x18\n\x10max_participants\x18\x04 \x01(\r\x12\x15\n\rcreation_time\x18\x05 \x01(\x03\x12\x15\n\rturn_password\x18\x06 \x01(\t\x12&\n\x0e\x65nabled_codecs\x18\x07 \x03(\x0b\x32\x0e.livekit.Codec\x12\x10\n\x08metadata\x18\x08 \x01(\t\x12\x18\n\x10num_participants\x18\t \x01(\r\x12\x16\n\x0enum_publishers\x18\x0b \x01(\r\x12\x18\n\x10\x61\x63tive_recording\x18\n \x01(\x08\x12&\n\x07version\x18\r \x01(\x0b\x32\x15.livekit.TimedVersion\"(\n\x05\x43odec\x12\x0c\n\x04mime\x18\x01 \x01(\t\x12\x11\n\tfmtp_line\x18\x02 \x01(\t\"9\n\x0cPlayoutDelay\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0b\n\x03min\x18\x02 \x01(\r\x12\x0b\n\x03max\x18\x03 \x01(\r\"\xe6\x01\n\x15ParticipantPermission\x12\x15\n\rcan_subscribe\x18\x01 \x01(\x08\x12\x13\n\x0b\x63\x61n_publish\x18\x02 \x01(\x08\x12\x18\n\x10\x63\x61n_publish_data\x18\x03 \x01(\x08\x12\x31\n\x13\x63\x61n_publish_sources\x18\t \x03(\x0e\x32\x14.livekit.TrackSource\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12\x14\n\x08recorder\x18\x08 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x13\x63\x61n_update_metadata\x18\n \x01(\x08\x12\x11\n\x05\x61gent\x18\x0b \x01(\x08\x42\x02\x18\x01\"\xc2\x04\n\x0fParticipantInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12-\n\x05state\x18\x03 \x01(\x0e\x32\x1e.livekit.ParticipantInfo.State\x12\"\n\x06tracks\x18\x04 \x03(\x0b\x32\x12.livekit.TrackInfo\x12\x10\n\x08metadata\x18\x05 \x01(\t\x12\x11\n\tjoined_at\x18\x06 \x01(\x03\x12\x0c\n\x04name\x18\t \x01(\t\x12\x0f\n\x07version\x18\n \x01(\r\x12\x32\n\npermission\x18\x0b \x01(\x0b\x32\x1e.livekit.ParticipantPermission\x12\x0e\n\x06region\x18\x0c \x01(\t\x12\x14\n\x0cis_publisher\x18\r \x01(\x08\x12+\n\x04kind\x18\x0e \x01(\x0e\x32\x1d.livekit.ParticipantInfo.Kind\x12<\n\nattributes\x18\x0f \x03(\x0b\x32(.livekit.ParticipantInfo.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\">\n\x05State\x12\x0b\n\x07JOINING\x10\x00\x12\n\n\x06JOINED\x10\x01\x12\n\n\x06\x41\x43TIVE\x10\x02\x12\x10\n\x0c\x44ISCONNECTED\x10\x03\"A\n\x04Kind\x12\x0c\n\x08STANDARD\x10\x00\x12\x0b\n\x07INGRESS\x10\x01\x12\n\n\x06\x45GRESS\x10\x02\x12\x07\n\x03SIP\x10\x03\x12\t\n\x05\x41GENT\x10\x04\"3\n\nEncryption\"%\n\x04Type\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03GCM\x10\x01\x12\n\n\x06\x43USTOM\x10\x02\"f\n\x12SimulcastCodecInfo\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x0b\n\x03mid\x18\x02 \x01(\t\x12\x0b\n\x03\x63id\x18\x03 \x01(\t\x12#\n\x06layers\x18\x04 \x03(\x0b\x32\x13.livekit.VideoLayer\"\xf5\x03\n\tTrackInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12 \n\x04type\x18\x02 \x01(\x0e\x32\x12.livekit.TrackType\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\r\n\x05muted\x18\x04 \x01(\x08\x12\r\n\x05width\x18\x05 \x01(\r\x12\x0e\n\x06height\x18\x06 \x01(\r\x12\x11\n\tsimulcast\x18\x07 \x01(\x08\x12\x13\n\x0b\x64isable_dtx\x18\x08 \x01(\x08\x12$\n\x06source\x18\t \x01(\x0e\x32\x14.livekit.TrackSource\x12#\n\x06layers\x18\n \x03(\x0b\x32\x13.livekit.VideoLayer\x12\x11\n\tmime_type\x18\x0b \x01(\t\x12\x0b\n\x03mid\x18\x0c \x01(\t\x12+\n\x06\x63odecs\x18\r \x03(\x0b\x32\x1b.livekit.SimulcastCodecInfo\x12\x0e\n\x06stereo\x18\x0e \x01(\x08\x12\x13\n\x0b\x64isable_red\x18\x0f \x01(\x08\x12,\n\nencryption\x18\x10 \x01(\x0e\x32\x18.livekit.Encryption.Type\x12\x0e\n\x06stream\x18\x11 \x01(\t\x12&\n\x07version\x18\x12 \x01(\x0b\x32\x15.livekit.TimedVersion\x12\x32\n\x0e\x61udio_features\x18\x13 \x03(\x0e\x32\x1a.livekit.AudioTrackFeature\"r\n\nVideoLayer\x12&\n\x07quality\x18\x01 \x01(\x0e\x32\x15.livekit.VideoQuality\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0f\n\x07\x62itrate\x18\x04 \x01(\r\x12\x0c\n\x04ssrc\x18\x05 \x01(\r\"\xd1\x02\n\nDataPacket\x12*\n\x04kind\x18\x01 \x01(\x0e\x32\x18.livekit.DataPacket.KindB\x02\x18\x01\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x1e\n\x16\x64\x65stination_identities\x18\x05 \x03(\t\x12#\n\x04user\x18\x02 \x01(\x0b\x32\x13.livekit.UserPacketH\x00\x12\x33\n\x07speaker\x18\x03 \x01(\x0b\x32\x1c.livekit.ActiveSpeakerUpdateB\x02\x18\x01H\x00\x12$\n\x08sip_dtmf\x18\x06 \x01(\x0b\x32\x10.livekit.SipDTMFH\x00\x12/\n\rtranscription\x18\x07 \x01(\x0b\x32\x16.livekit.TranscriptionH\x00\"\x1f\n\x04Kind\x12\x0c\n\x08RELIABLE\x10\x00\x12\t\n\x05LOSSY\x10\x01\x42\x07\n\x05value\"=\n\x13\x41\x63tiveSpeakerUpdate\x12&\n\x08speakers\x18\x01 \x03(\x0b\x32\x14.livekit.SpeakerInfo\"9\n\x0bSpeakerInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\r\n\x05level\x18\x02 \x01(\x02\x12\x0e\n\x06\x61\x63tive\x18\x03 \x01(\x08\"\xa0\x02\n\nUserPacket\x12\x1b\n\x0fparticipant_sid\x18\x01 \x01(\tB\x02\x18\x01\x12 \n\x14participant_identity\x18\x05 \x01(\tB\x02\x18\x01\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x1c\n\x10\x64\x65stination_sids\x18\x03 \x03(\tB\x02\x18\x01\x12\"\n\x16\x64\x65stination_identities\x18\x06 \x03(\tB\x02\x18\x01\x12\x12\n\x05topic\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nstart_time\x18\t \x01(\x04H\x02\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\n \x01(\x04H\x03\x88\x01\x01\x42\x08\n\x06_topicB\x05\n\x03_idB\r\n\x0b_start_timeB\x0b\n\t_end_time\"&\n\x07SipDTMF\x12\x0c\n\x04\x63ode\x18\x03 \x01(\r\x12\r\n\x05\x64igit\x18\x04 \x01(\t\"|\n\rTranscription\x12(\n transcribed_participant_identity\x18\x02 \x01(\t\x12\x10\n\x08track_id\x18\x03 \x01(\t\x12/\n\x08segments\x18\x04 \x03(\x0b\x32\x1d.livekit.TranscriptionSegment\"w\n\x14TranscriptionSegment\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x02 \x01(\t\x12\x12\n\nstart_time\x18\x03 \x01(\x04\x12\x10\n\x08\x65nd_time\x18\x04 \x01(\x04\x12\r\n\x05\x66inal\x18\x05 \x01(\x08\x12\x10\n\x08language\x18\x06 \x01(\t\"@\n\x11ParticipantTracks\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x12\n\ntrack_sids\x18\x02 \x03(\t\"\xce\x01\n\nServerInfo\x12,\n\x07\x65\x64ition\x18\x01 \x01(\x0e\x32\x1b.livekit.ServerInfo.Edition\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x10\n\x08protocol\x18\x03 \x01(\x05\x12\x0e\n\x06region\x18\x04 \x01(\t\x12\x0f\n\x07node_id\x18\x05 \x01(\t\x12\x12\n\ndebug_info\x18\x06 \x01(\t\x12\x16\n\x0e\x61gent_protocol\x18\x07 \x01(\x05\"\"\n\x07\x45\x64ition\x12\x0c\n\x08Standard\x10\x00\x12\t\n\x05\x43loud\x10\x01\"\xdd\x02\n\nClientInfo\x12$\n\x03sdk\x18\x01 \x01(\x0e\x32\x17.livekit.ClientInfo.SDK\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x10\n\x08protocol\x18\x03 \x01(\x05\x12\n\n\x02os\x18\x04 \x01(\t\x12\x12\n\nos_version\x18\x05 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x06 \x01(\t\x12\x0f\n\x07\x62rowser\x18\x07 \x01(\t\x12\x17\n\x0f\x62rowser_version\x18\x08 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\t \x01(\t\x12\x0f\n\x07network\x18\n \x01(\t\"\x83\x01\n\x03SDK\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x06\n\x02JS\x10\x01\x12\t\n\x05SWIFT\x10\x02\x12\x0b\n\x07\x41NDROID\x10\x03\x12\x0b\n\x07\x46LUTTER\x10\x04\x12\x06\n\x02GO\x10\x05\x12\t\n\x05UNITY\x10\x06\x12\x10\n\x0cREACT_NATIVE\x10\x07\x12\x08\n\x04RUST\x10\x08\x12\n\n\x06PYTHON\x10\t\x12\x07\n\x03\x43PP\x10\n\"\x8c\x02\n\x13\x43lientConfiguration\x12*\n\x05video\x18\x01 \x01(\x0b\x32\x1b.livekit.VideoConfiguration\x12+\n\x06screen\x18\x02 \x01(\x0b\x32\x1b.livekit.VideoConfiguration\x12\x37\n\x11resume_connection\x18\x03 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\x12\x30\n\x0f\x64isabled_codecs\x18\x04 \x01(\x0b\x32\x17.livekit.DisabledCodecs\x12\x31\n\x0b\x66orce_relay\x18\x05 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\"L\n\x12VideoConfiguration\x12\x36\n\x10hardware_encoder\x18\x01 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\"Q\n\x0e\x44isabledCodecs\x12\x1e\n\x06\x63odecs\x18\x01 \x03(\x0b\x32\x0e.livekit.Codec\x12\x1f\n\x07publish\x18\x02 \x03(\x0b\x32\x0e.livekit.Codec\"\x80\x02\n\x08RTPDrift\x12.\n\nstart_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08\x64uration\x18\x03 \x01(\x01\x12\x17\n\x0fstart_timestamp\x18\x04 \x01(\x04\x12\x15\n\rend_timestamp\x18\x05 \x01(\x04\x12\x17\n\x0frtp_clock_ticks\x18\x06 \x01(\x04\x12\x15\n\rdrift_samples\x18\x07 \x01(\x03\x12\x10\n\x08\x64rift_ms\x18\x08 \x01(\x01\x12\x12\n\nclock_rate\x18\t \x01(\x01\"\xa0\n\n\x08RTPStats\x12.\n\nstart_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08\x64uration\x18\x03 \x01(\x01\x12\x0f\n\x07packets\x18\x04 \x01(\r\x12\x13\n\x0bpacket_rate\x18\x05 \x01(\x01\x12\r\n\x05\x62ytes\x18\x06 \x01(\x04\x12\x14\n\x0cheader_bytes\x18\' \x01(\x04\x12\x0f\n\x07\x62itrate\x18\x07 \x01(\x01\x12\x14\n\x0cpackets_lost\x18\x08 \x01(\r\x12\x18\n\x10packet_loss_rate\x18\t \x01(\x01\x12\x1e\n\x16packet_loss_percentage\x18\n \x01(\x02\x12\x19\n\x11packets_duplicate\x18\x0b \x01(\r\x12\x1d\n\x15packet_duplicate_rate\x18\x0c \x01(\x01\x12\x17\n\x0f\x62ytes_duplicate\x18\r \x01(\x04\x12\x1e\n\x16header_bytes_duplicate\x18( \x01(\x04\x12\x19\n\x11\x62itrate_duplicate\x18\x0e \x01(\x01\x12\x17\n\x0fpackets_padding\x18\x0f \x01(\r\x12\x1b\n\x13packet_padding_rate\x18\x10 \x01(\x01\x12\x15\n\rbytes_padding\x18\x11 \x01(\x04\x12\x1c\n\x14header_bytes_padding\x18) \x01(\x04\x12\x17\n\x0f\x62itrate_padding\x18\x12 \x01(\x01\x12\x1c\n\x14packets_out_of_order\x18\x13 \x01(\r\x12\x0e\n\x06\x66rames\x18\x14 \x01(\r\x12\x12\n\nframe_rate\x18\x15 \x01(\x01\x12\x16\n\x0ejitter_current\x18\x16 \x01(\x01\x12\x12\n\njitter_max\x18\x17 \x01(\x01\x12:\n\rgap_histogram\x18\x18 \x03(\x0b\x32#.livekit.RTPStats.GapHistogramEntry\x12\r\n\x05nacks\x18\x19 \x01(\r\x12\x11\n\tnack_acks\x18% \x01(\r\x12\x13\n\x0bnack_misses\x18\x1a \x01(\r\x12\x15\n\rnack_repeated\x18& \x01(\r\x12\x0c\n\x04plis\x18\x1b \x01(\r\x12,\n\x08last_pli\x18\x1c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04\x66irs\x18\x1d \x01(\r\x12,\n\x08last_fir\x18\x1e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x13\n\x0brtt_current\x18\x1f \x01(\r\x12\x0f\n\x07rtt_max\x18 \x01(\r\x12\x12\n\nkey_frames\x18! \x01(\r\x12\x32\n\x0elast_key_frame\x18\" \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0flayer_lock_plis\x18# \x01(\r\x12\x37\n\x13last_layer_lock_pli\x18$ \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x0cpacket_drift\x18, \x01(\x0b\x32\x11.livekit.RTPDrift\x12\'\n\x0creport_drift\x18- \x01(\x0b\x32\x11.livekit.RTPDrift\x12/\n\x14rebased_report_drift\x18. \x01(\x0b\x32\x11.livekit.RTPDrift\x1a\x33\n\x11GapHistogramEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"1\n\x0cTimedVersion\x12\x12\n\nunix_micro\x18\x01 \x01(\x03\x12\r\n\x05ticks\x18\x02 \x01(\x05*/\n\nAudioCodec\x12\x0e\n\nDEFAULT_AC\x10\x00\x12\x08\n\x04OPUS\x10\x01\x12\x07\n\x03\x41\x41\x43\x10\x02*V\n\nVideoCodec\x12\x0e\n\nDEFAULT_VC\x10\x00\x12\x11\n\rH264_BASELINE\x10\x01\x12\r\n\tH264_MAIN\x10\x02\x12\r\n\tH264_HIGH\x10\x03\x12\x07\n\x03VP8\x10\x04*)\n\nImageCodec\x12\x0e\n\nIC_DEFAULT\x10\x00\x12\x0b\n\x07IC_JPEG\x10\x01*+\n\tTrackType\x12\t\n\x05\x41UDIO\x10\x00\x12\t\n\x05VIDEO\x10\x01\x12\x08\n\x04\x44\x41TA\x10\x02*`\n\x0bTrackSource\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06\x43\x41MERA\x10\x01\x12\x0e\n\nMICROPHONE\x10\x02\x12\x10\n\x0cSCREEN_SHARE\x10\x03\x12\x16\n\x12SCREEN_SHARE_AUDIO\x10\x04*6\n\x0cVideoQuality\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\x12\x07\n\x03OFF\x10\x03*@\n\x11\x43onnectionQuality\x12\x08\n\x04POOR\x10\x00\x12\x08\n\x04GOOD\x10\x01\x12\r\n\tEXCELLENT\x10\x02\x12\x08\n\x04LOST\x10\x03*;\n\x13\x43lientConfigSetting\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x44ISABLED\x10\x01\x12\x0b\n\x07\x45NABLED\x10\x02*\xdb\x01\n\x10\x44isconnectReason\x12\x12\n\x0eUNKNOWN_REASON\x10\x00\x12\x14\n\x10\x43LIENT_INITIATED\x10\x01\x12\x16\n\x12\x44UPLICATE_IDENTITY\x10\x02\x12\x13\n\x0fSERVER_SHUTDOWN\x10\x03\x12\x17\n\x13PARTICIPANT_REMOVED\x10\x04\x12\x10\n\x0cROOM_DELETED\x10\x05\x12\x12\n\x0eSTATE_MISMATCH\x10\x06\x12\x10\n\x0cJOIN_FAILURE\x10\x07\x12\r\n\tMIGRATION\x10\x08\x12\x10\n\x0cSIGNAL_CLOSE\x10\t*\x89\x01\n\x0fReconnectReason\x12\x0e\n\nRR_UNKNOWN\x10\x00\x12\x1a\n\x16RR_SIGNAL_DISCONNECTED\x10\x01\x12\x17\n\x13RR_PUBLISHER_FAILED\x10\x02\x12\x18\n\x14RR_SUBSCRIBER_FAILED\x10\x03\x12\x17\n\x13RR_SWITCH_CANDIDATE\x10\x04*T\n\x11SubscriptionError\x12\x0e\n\nSE_UNKNOWN\x10\x00\x12\x18\n\x14SE_CODEC_UNSUPPORTED\x10\x01\x12\x15\n\x11SE_TRACK_NOTFOUND\x10\x02*\xa3\x01\n\x11\x41udioTrackFeature\x12\r\n\tTF_STEREO\x10\x00\x12\r\n\tTF_NO_DTX\x10\x01\x12\x18\n\x14TF_AUTO_GAIN_CONTROL\x10\x02\x12\x18\n\x14TF_ECHO_CANCELLATION\x10\x03\x12\x18\n\x14TF_NOISE_SUPPRESSION\x10\x04\x12\"\n\x1eTF_ENHANCED_NOISE_CANCELLATION\x10\x05\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14livekit_models.proto\x12\x07livekit\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x15livekit_metrics.proto\x1a\x14logger/options.proto\"9\n\nPagination\x12\x1c\n\x08\x61\x66ter_id\x18\x01 \x01(\tB\n\xbaP\x07\x61\x66terID\x12\r\n\x05limit\x18\x02 \x01(\x05\" \n\x0fTokenPagination\x12\r\n\x05token\x18\x01 \x01(\t\"E\n\nListUpdate\x12\x0b\n\x03set\x18\x01 \x03(\t\x12\x0b\n\x03\x61\x64\x64\x18\x02 \x03(\t\x12\x0e\n\x06remove\x18\x03 \x03(\t\x12\r\n\x05\x63lear\x18\x04 \x01(\x08\"\x89\x03\n\x04Room\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x15\n\rempty_timeout\x18\x03 \x01(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\x0e \x01(\r\x12\x18\n\x10max_participants\x18\x04 \x01(\r\x12\x15\n\rcreation_time\x18\x05 \x01(\x03\x12\x18\n\x10\x63reation_time_ms\x18\x0f \x01(\x03\x12\x15\n\rturn_password\x18\x06 \x01(\t\x12&\n\x0e\x65nabled_codecs\x18\x07 \x03(\x0b\x32\x0e.livekit.Codec\x12\x36\n\x08metadata\x18\x08 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x18\n\x10num_participants\x18\t \x01(\r\x12\x16\n\x0enum_publishers\x18\x0b \x01(\r\x12\x18\n\x10\x61\x63tive_recording\x18\n \x01(\x08\x12&\n\x07version\x18\r \x01(\x0b\x32\x15.livekit.TimedVersion\"(\n\x05\x43odec\x12\x0c\n\x04mime\x18\x01 \x01(\t\x12\x11\n\tfmtp_line\x18\x02 \x01(\t\"9\n\x0cPlayoutDelay\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\x12\x0b\n\x03min\x18\x02 \x01(\r\x12\x0b\n\x03max\x18\x03 \x01(\r\"\xa7\x02\n\x15ParticipantPermission\x12\x15\n\rcan_subscribe\x18\x01 \x01(\x08\x12\x13\n\x0b\x63\x61n_publish\x18\x02 \x01(\x08\x12\x18\n\x10\x63\x61n_publish_data\x18\x03 \x01(\x08\x12\x31\n\x13\x63\x61n_publish_sources\x18\t \x03(\x0e\x32\x14.livekit.TrackSource\x12\x0e\n\x06hidden\x18\x07 \x01(\x08\x12\x14\n\x08recorder\x18\x08 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x13\x63\x61n_update_metadata\x18\n \x01(\x08\x12\x11\n\x05\x61gent\x18\x0b \x01(\x08\x42\x02\x18\x01\x12\x1d\n\x15\x63\x61n_subscribe_metrics\x18\x0c \x01(\x08\x12 \n\x18\x63\x61n_manage_agent_session\x18\r \x01(\x08\"\xae\x08\n\x0fParticipantInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12-\n\x05state\x18\x03 \x01(\x0e\x32\x1e.livekit.ParticipantInfo.State\x12\"\n\x06tracks\x18\x04 \x03(\x0b\x32\x12.livekit.TrackInfo\x12\x36\n\x08metadata\x18\x05 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x11\n\tjoined_at\x18\x06 \x01(\x03\x12\x14\n\x0cjoined_at_ms\x18\x11 \x01(\x03\x12\x11\n\x04name\x18\t \x01(\tB\x03\xc0P\x01\x12\x0f\n\x07version\x18\n \x01(\r\x12\x32\n\npermission\x18\x0b \x01(\x0b\x32\x1e.livekit.ParticipantPermission\x12\x0e\n\x06region\x18\x0c \x01(\t\x12\x14\n\x0cis_publisher\x18\r \x01(\x08\x12+\n\x04kind\x18\x0e \x01(\x0e\x32\x1d.livekit.ParticipantInfo.Kind\x12\x62\n\nattributes\x18\x0f \x03(\x0b\x32(.livekit.ParticipantInfo.AttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x34\n\x11\x64isconnect_reason\x18\x10 \x01(\x0e\x32\x19.livekit.DisconnectReason\x12\x39\n\x0ckind_details\x18\x12 \x03(\x0e\x32#.livekit.ParticipantInfo.KindDetail\x12+\n\x0b\x64\x61ta_tracks\x18\x13 \x03(\x0b\x32\x16.livekit.DataTrackInfo\x12\x17\n\x0f\x63lient_protocol\x18\x14 \x01(\x05\x12\x34\n\x0c\x63\x61pabilities\x18\x15 \x03(\x0e\x32\x1e.livekit.ClientInfo.Capability\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\">\n\x05State\x12\x0b\n\x07JOINING\x10\x00\x12\n\n\x06JOINED\x10\x01\x12\n\n\x06\x41\x43TIVE\x10\x02\x12\x10\n\x0c\x44ISCONNECTED\x10\x03\"\\\n\x04Kind\x12\x0c\n\x08STANDARD\x10\x00\x12\x0b\n\x07INGRESS\x10\x01\x12\n\n\x06\x45GRESS\x10\x02\x12\x07\n\x03SIP\x10\x03\x12\t\n\x05\x41GENT\x10\x04\x12\r\n\tCONNECTOR\x10\x07\x12\n\n\x06\x42RIDGE\x10\x08\"{\n\nKindDetail\x12\x0f\n\x0b\x43LOUD_AGENT\x10\x00\x12\r\n\tFORWARDED\x10\x01\x12\x16\n\x12\x43ONNECTOR_WHATSAPP\x10\x02\x12\x14\n\x10\x43ONNECTOR_TWILIO\x10\x03\x12\x0f\n\x0b\x42RIDGE_RTSP\x10\x04\x12\x0e\n\nSIMULATION\x10\x05\"3\n\nEncryption\"%\n\x04Type\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03GCM\x10\x01\x12\n\n\x06\x43USTOM\x10\x02\"\xab\x01\n\x12SimulcastCodecInfo\x12\x11\n\tmime_type\x18\x01 \x01(\t\x12\x0b\n\x03mid\x18\x02 \x01(\t\x12\x0b\n\x03\x63id\x18\x03 \x01(\t\x12#\n\x06layers\x18\x04 \x03(\x0b\x32\x13.livekit.VideoLayer\x12\x32\n\x10video_layer_mode\x18\x05 \x01(\x0e\x32\x18.livekit.VideoLayer.Mode\x12\x0f\n\x07sdp_cid\x18\x06 \x01(\t\"\x83\x05\n\tTrackInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12 \n\x04type\x18\x02 \x01(\x0e\x32\x12.livekit.TrackType\x12\x11\n\x04name\x18\x03 \x01(\tB\x03\xc0P\x01\x12\r\n\x05muted\x18\x04 \x01(\x08\x12\r\n\x05width\x18\x05 \x01(\r\x12\x0e\n\x06height\x18\x06 \x01(\r\x12\x15\n\tsimulcast\x18\x07 \x01(\x08\x42\x02\x18\x01\x12\x17\n\x0b\x64isable_dtx\x18\x08 \x01(\x08\x42\x02\x18\x01\x12$\n\x06source\x18\t \x01(\x0e\x32\x14.livekit.TrackSource\x12\'\n\x06layers\x18\n \x03(\x0b\x32\x13.livekit.VideoLayerB\x02\x18\x01\x12\x11\n\tmime_type\x18\x0b \x01(\t\x12\x0b\n\x03mid\x18\x0c \x01(\t\x12+\n\x06\x63odecs\x18\r \x03(\x0b\x32\x1b.livekit.SimulcastCodecInfo\x12\x12\n\x06stereo\x18\x0e \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0b\x64isable_red\x18\x0f \x01(\x08\x12,\n\nencryption\x18\x10 \x01(\x0e\x32\x18.livekit.Encryption.Type\x12\x0e\n\x06stream\x18\x11 \x01(\t\x12&\n\x07version\x18\x12 \x01(\x0b\x32\x15.livekit.TimedVersion\x12\x32\n\x0e\x61udio_features\x18\x13 \x03(\x0e\x32\x1a.livekit.AudioTrackFeature\x12\x37\n\x13\x62\x61\x63kup_codec_policy\x18\x14 \x01(\x0e\x32\x1a.livekit.BackupCodecPolicy\x12>\n\x17packet_trailer_features\x18\x15 \x03(\x0e\x32\x1d.livekit.PacketTrailerFeature\"\xf9\x01\n\rDataTrackInfo\x12\x12\n\npub_handle\x18\x01 \x01(\r\x12\x0b\n\x03sid\x18\x02 \x01(\t\x12\x0c\n\x04name\x18\x03 \x01(\t\x12,\n\nencryption\x18\x04 \x01(\x0e\x32\x18.livekit.Encryption.Type\x12<\n\x0e\x66rame_encoding\x18\x05 \x01(\x0b\x32\x1f.livekit.DataTrackFrameEncodingH\x00\x88\x01\x01\x12/\n\x06schema\x18\x06 \x01(\x0b\x32\x1a.livekit.DataTrackSchemaIdH\x01\x88\x01\x01\x42\x11\n\x0f_frame_encodingB\t\n\x07_schema\"\xcf\x03\n\x16\x44\x61taTrackFrameEncoding\x12L\n\nwell_known\x18\x01 \x01(\x0e\x32\x36.livekit.DataTrackFrameEncoding.WellKnownFrameEncodingH\x00\x12\x10\n\x06\x63ustom\x18\x02 \x01(\tH\x00\"\xcb\x02\n\x16WellKnownFrameEncoding\x12)\n%WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED\x10\x00\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_ROS1\x10\x01\x12!\n\x1dWELL_KNOWN_FRAME_ENCODING_CDR\x10\x02\x12&\n\"WELL_KNOWN_FRAME_ENCODING_PROTOBUF\x10\x03\x12(\n$WELL_KNOWN_FRAME_ENCODING_FLATBUFFER\x10\x04\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_CBOR\x10\x05\x12%\n!WELL_KNOWN_FRAME_ENCODING_MSGPACK\x10\x06\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_JSON\x10\x07\x42\x07\n\x05value\"\xef\x03\n\x17\x44\x61taTrackSchemaEncoding\x12N\n\nwell_known\x18\x01 \x01(\x0e\x32\x38.livekit.DataTrackSchemaEncoding.WellKnownSchemaEncodingH\x00\x12\x10\n\x06\x63ustom\x18\x02 \x01(\tH\x00\"\xe8\x02\n\x17WellKnownSchemaEncoding\x12*\n&WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED\x10\x00\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF\x10\x01\x12)\n%WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER\x10\x02\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG\x10\x03\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG\x10\x04\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL\x10\x05\x12&\n\"WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL\x10\x06\x12*\n&WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA\x10\x07\x42\x07\n\x05value\"U\n\x11\x44\x61taTrackSchemaId\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x32\n\x08\x65ncoding\x18\x02 \x01(\x0b\x32 .livekit.DataTrackSchemaEncoding\"f\n DataTrackExtensionParticipantSid\x12)\n\x02id\x18\x01 \x01(\x0e\x32\x1d.livekit.DataTrackExtensionID\x12\x17\n\x0fparticipant_sid\x18\x02 \x01(\t\"F\n\x1c\x44\x61taTrackSubscriptionOptions\x12\x17\n\ntarget_fps\x18\x01 \x01(\rH\x00\x88\x01\x01\x42\r\n\x0b_target_fps\"e\n\x0b\x44\x61taBlobKey\x12\x11\n\x07generic\x18\x01 \x01(\tH\x00\x12<\n\tschema_id\x18\x02 \x01(\x0b\x32\x1a.livekit.DataTrackSchemaIdB\x0b\xbaP\x08schemaIDH\x00\x42\x05\n\x03key\"e\n\x08\x44\x61taBlob\x12!\n\x03key\x18\x01 \x01(\x0b\x32\x14.livekit.DataBlobKey\x12\x36\n\x08\x63ontents\x18\x02 \x01(\x0c\x42$\xb2P\x1e\xc0P\x01\"\xc4\x02\n\nVideoLayer\x12&\n\x07quality\x18\x01 \x01(\x0e\x32\x15.livekit.VideoQuality\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0f\n\x07\x62itrate\x18\x04 \x01(\r\x12\x0c\n\x04ssrc\x18\x05 \x01(\r\x12\x15\n\rspatial_layer\x18\x06 \x01(\x05\x12\x0b\n\x03rid\x18\x07 \x01(\t\x12\x13\n\x0brepair_ssrc\x18\x08 \x01(\r\"\x96\x01\n\x04Mode\x12\x0f\n\x0bMODE_UNUSED\x10\x00\x12 \n\x1cONE_SPATIAL_LAYER_PER_STREAM\x10\x01\x12&\n\"MULTIPLE_SPATIAL_LAYERS_PER_STREAM\x10\x02\x12\x33\n/ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR\x10\x03\"\xa7\x06\n\nDataPacket\x12*\n\x04kind\x18\x01 \x01(\x0e\x32\x18.livekit.DataPacket.KindB\x02\x18\x01\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x1e\n\x16\x64\x65stination_identities\x18\x05 \x03(\t\x12#\n\x04user\x18\x02 \x01(\x0b\x32\x13.livekit.UserPacketH\x00\x12\x33\n\x07speaker\x18\x03 \x01(\x0b\x32\x1c.livekit.ActiveSpeakerUpdateB\x02\x18\x01H\x00\x12$\n\x08sip_dtmf\x18\x06 \x01(\x0b\x32\x10.livekit.SipDTMFH\x00\x12/\n\rtranscription\x18\x07 \x01(\x0b\x32\x16.livekit.TranscriptionH\x00\x12(\n\x07metrics\x18\x08 \x01(\x0b\x32\x15.livekit.MetricsBatchH\x00\x12,\n\x0c\x63hat_message\x18\t \x01(\x0b\x32\x14.livekit.ChatMessageH\x00\x12*\n\x0brpc_request\x18\n \x01(\x0b\x32\x13.livekit.RpcRequestH\x00\x12\"\n\x07rpc_ack\x18\x0b \x01(\x0b\x32\x0f.livekit.RpcAckH\x00\x12,\n\x0crpc_response\x18\x0c \x01(\x0b\x32\x14.livekit.RpcResponseH\x00\x12\x33\n\rstream_header\x18\r \x01(\x0b\x32\x1a.livekit.DataStream.HeaderH\x00\x12\x31\n\x0cstream_chunk\x18\x0e \x01(\x0b\x32\x19.livekit.DataStream.ChunkH\x00\x12\x35\n\x0estream_trailer\x18\x0f \x01(\x0b\x32\x1b.livekit.DataStream.TrailerH\x00\x12\x34\n\x10\x65ncrypted_packet\x18\x12 \x01(\x0b\x32\x18.livekit.EncryptedPacketH\x00\x12\x10\n\x08sequence\x18\x10 \x01(\r\x12\x17\n\x0fparticipant_sid\x18\x11 \x01(\t\"\x1f\n\x04Kind\x12\x0c\n\x08RELIABLE\x10\x00\x12\t\n\x05LOSSY\x10\x01\x42\x07\n\x05value\"|\n\x0f\x45ncryptedPacket\x12\x31\n\x0f\x65ncryption_type\x18\x01 \x01(\x0e\x32\x18.livekit.Encryption.Type\x12\n\n\x02iv\x18\x02 \x01(\x0c\x12\x11\n\tkey_index\x18\x03 \x01(\r\x12\x17\n\x0f\x65ncrypted_value\x18\x04 \x01(\x0c\"\x91\x03\n\x16\x45ncryptedPacketPayload\x12#\n\x04user\x18\x01 \x01(\x0b\x32\x13.livekit.UserPacketH\x00\x12,\n\x0c\x63hat_message\x18\x03 \x01(\x0b\x32\x14.livekit.ChatMessageH\x00\x12*\n\x0brpc_request\x18\x04 \x01(\x0b\x32\x13.livekit.RpcRequestH\x00\x12\"\n\x07rpc_ack\x18\x05 \x01(\x0b\x32\x0f.livekit.RpcAckH\x00\x12,\n\x0crpc_response\x18\x06 \x01(\x0b\x32\x14.livekit.RpcResponseH\x00\x12\x33\n\rstream_header\x18\x07 \x01(\x0b\x32\x1a.livekit.DataStream.HeaderH\x00\x12\x31\n\x0cstream_chunk\x18\x08 \x01(\x0b\x32\x19.livekit.DataStream.ChunkH\x00\x12\x35\n\x0estream_trailer\x18\t \x01(\x0b\x32\x1b.livekit.DataStream.TrailerH\x00\x42\x07\n\x05value\"A\n\x13\x41\x63tiveSpeakerUpdate\x12&\n\x08speakers\x18\x01 \x03(\x0b\x32\x14.livekit.SpeakerInfo:\x02\x18\x01\"9\n\x0bSpeakerInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\r\n\x05level\x18\x02 \x01(\x02\x12\x0e\n\x06\x61\x63tive\x18\x03 \x01(\x08\"\xaf\x02\n\nUserPacket\x12\x1b\n\x0fparticipant_sid\x18\x01 \x01(\tB\x02\x18\x01\x12 \n\x14participant_identity\x18\x05 \x01(\tB\x02\x18\x01\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\x12\x1c\n\x10\x64\x65stination_sids\x18\x03 \x03(\tB\x02\x18\x01\x12\"\n\x16\x64\x65stination_identities\x18\x06 \x03(\tB\x02\x18\x01\x12\x12\n\x05topic\x18\x04 \x01(\tH\x00\x88\x01\x01\x12\x0f\n\x02id\x18\x08 \x01(\tH\x01\x88\x01\x01\x12\x17\n\nstart_time\x18\t \x01(\x04H\x02\x88\x01\x01\x12\x15\n\x08\x65nd_time\x18\n \x01(\x04H\x03\x88\x01\x01\x12\r\n\x05nonce\x18\x0b \x01(\x0c\x42\x08\n\x06_topicB\x05\n\x03_idB\r\n\x0b_start_timeB\x0b\n\t_end_time\"&\n\x07SipDTMF\x12\x0c\n\x04\x63ode\x18\x03 \x01(\r\x12\r\n\x05\x64igit\x18\x04 \x01(\t\"\x88\x01\n\rTranscription\x12(\n transcribed_participant_identity\x18\x02 \x01(\t\x12\x1c\n\x08track_id\x18\x03 \x01(\tB\n\xbaP\x07trackID\x12/\n\x08segments\x18\x04 \x03(\x0b\x32\x1d.livekit.TranscriptionSegment\"w\n\x14TranscriptionSegment\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x02 \x01(\t\x12\x12\n\nstart_time\x18\x03 \x01(\x04\x12\x10\n\x08\x65nd_time\x18\x04 \x01(\x04\x12\r\n\x05\x66inal\x18\x05 \x01(\x08\x12\x10\n\x08language\x18\x06 \x01(\t\"\x91\x01\n\x0b\x43hatMessage\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\x1b\n\x0e\x65\x64it_timestamp\x18\x03 \x01(\x03H\x00\x88\x01\x01\x12\x0f\n\x07message\x18\x04 \x01(\t\x12\x0f\n\x07\x64\x65leted\x18\x05 \x01(\x08\x12\x11\n\tgenerated\x18\x06 \x01(\x08\x42\x11\n\x0f_edit_timestamp\"\x83\x01\n\nRpcRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0e\n\x06method\x18\x02 \x01(\t\x12\x0f\n\x07payload\x18\x03 \x01(\t\x12\x1b\n\x13response_timeout_ms\x18\x04 \x01(\r\x12\x0f\n\x07version\x18\x05 \x01(\r\x12\x1a\n\x12\x63ompressed_payload\x18\x06 \x01(\x0c\"*\n\x06RpcAck\x12 \n\nrequest_id\x18\x01 \x01(\tB\x0c\xbaP\trequestID\"\x8d\x01\n\x0bRpcResponse\x12 \n\nrequest_id\x18\x01 \x01(\tB\x0c\xbaP\trequestID\x12\x11\n\x07payload\x18\x02 \x01(\tH\x00\x12\"\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x11.livekit.RpcErrorH\x00\x12\x1c\n\x12\x63ompressed_payload\x18\x04 \x01(\x0cH\x00\x42\x07\n\x05value\"7\n\x08RpcError\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\"@\n\x11ParticipantTracks\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x12\n\ntrack_sids\x18\x02 \x03(\t\"\xd9\x01\n\nServerInfo\x12,\n\x07\x65\x64ition\x18\x01 \x01(\x0e\x32\x1b.livekit.ServerInfo.Edition\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x10\n\x08protocol\x18\x03 \x01(\x05\x12\x0e\n\x06region\x18\x04 \x01(\t\x12\x1a\n\x07node_id\x18\x05 \x01(\tB\t\xbaP\x06nodeID\x12\x12\n\ndebug_info\x18\x06 \x01(\t\x12\x16\n\x0e\x61gent_protocol\x18\x07 \x01(\x05\"\"\n\x07\x45\x64ition\x12\x0c\n\x08Standard\x10\x00\x12\t\n\x05\x43loud\x10\x01\"\xc7\x04\n\nClientInfo\x12$\n\x03sdk\x18\x01 \x01(\x0e\x32\x17.livekit.ClientInfo.SDK\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x10\n\x08protocol\x18\x03 \x01(\x05\x12\n\n\x02os\x18\x04 \x01(\t\x12\x12\n\nos_version\x18\x05 \x01(\t\x12\x14\n\x0c\x64\x65vice_model\x18\x06 \x01(\t\x12\x0f\n\x07\x62rowser\x18\x07 \x01(\t\x12\x17\n\x0f\x62rowser_version\x18\x08 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\t \x01(\t\x12\x0f\n\x07network\x18\n \x01(\t\x12\x12\n\nother_sdks\x18\x0b \x01(\t\x12\x17\n\x0f\x63lient_protocol\x18\x0c \x01(\x05\x12\x34\n\x0c\x63\x61pabilities\x18\r \x03(\x0e\x32\x1e.livekit.ClientInfo.Capability\"\xb3\x01\n\x03SDK\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x06\n\x02JS\x10\x01\x12\t\n\x05SWIFT\x10\x02\x12\x0b\n\x07\x41NDROID\x10\x03\x12\x0b\n\x07\x46LUTTER\x10\x04\x12\x06\n\x02GO\x10\x05\x12\t\n\x05UNITY\x10\x06\x12\x10\n\x0cREACT_NATIVE\x10\x07\x12\x08\n\x04RUST\x10\x08\x12\n\n\x06PYTHON\x10\t\x12\x07\n\x03\x43PP\x10\n\x12\r\n\tUNITY_WEB\x10\x0b\x12\x08\n\x04NODE\x10\x0c\x12\n\n\x06UNREAL\x10\r\x12\t\n\x05\x45SP32\x10\x0e\"U\n\nCapability\x12\x0e\n\nCAP_UNUSED\x10\x00\x12\x16\n\x12\x43\x41P_PACKET_TRAILER\x10\x01\x12\x1f\n\x1b\x43\x41P_COMPRESSION_DEFLATE_RAW\x10\x02\"\x8c\x02\n\x13\x43lientConfiguration\x12*\n\x05video\x18\x01 \x01(\x0b\x32\x1b.livekit.VideoConfiguration\x12+\n\x06screen\x18\x02 \x01(\x0b\x32\x1b.livekit.VideoConfiguration\x12\x37\n\x11resume_connection\x18\x03 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\x12\x30\n\x0f\x64isabled_codecs\x18\x04 \x01(\x0b\x32\x17.livekit.DisabledCodecs\x12\x31\n\x0b\x66orce_relay\x18\x05 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\"L\n\x12VideoConfiguration\x12\x36\n\x10hardware_encoder\x18\x01 \x01(\x0e\x32\x1c.livekit.ClientConfigSetting\"Q\n\x0e\x44isabledCodecs\x12\x1e\n\x06\x63odecs\x18\x01 \x03(\x0b\x32\x0e.livekit.Codec\x12\x1f\n\x07publish\x18\x02 \x03(\x0b\x32\x0e.livekit.Codec\"\x80\x02\n\x08RTPDrift\x12.\n\nstart_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08\x64uration\x18\x03 \x01(\x01\x12\x17\n\x0fstart_timestamp\x18\x04 \x01(\x04\x12\x15\n\rend_timestamp\x18\x05 \x01(\x04\x12\x17\n\x0frtp_clock_ticks\x18\x06 \x01(\x04\x12\x15\n\rdrift_samples\x18\x07 \x01(\x03\x12\x10\n\x08\x64rift_ms\x18\x08 \x01(\x01\x12\x12\n\nclock_rate\x18\t \x01(\x01\"\xd6\n\n\x08RTPStats\x12.\n\nstart_time\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12,\n\x08\x65nd_time\x18\x02 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x10\n\x08\x64uration\x18\x03 \x01(\x01\x12\x0f\n\x07packets\x18\x04 \x01(\r\x12\x13\n\x0bpacket_rate\x18\x05 \x01(\x01\x12\r\n\x05\x62ytes\x18\x06 \x01(\x04\x12\x14\n\x0cheader_bytes\x18\' \x01(\x04\x12\x0f\n\x07\x62itrate\x18\x07 \x01(\x01\x12\x14\n\x0cpackets_lost\x18\x08 \x01(\r\x12\x18\n\x10packet_loss_rate\x18\t \x01(\x01\x12\x1e\n\x16packet_loss_percentage\x18\n \x01(\x02\x12\x19\n\x11packets_duplicate\x18\x0b \x01(\r\x12\x1d\n\x15packet_duplicate_rate\x18\x0c \x01(\x01\x12\x17\n\x0f\x62ytes_duplicate\x18\r \x01(\x04\x12\x1e\n\x16header_bytes_duplicate\x18( \x01(\x04\x12\x19\n\x11\x62itrate_duplicate\x18\x0e \x01(\x01\x12\x17\n\x0fpackets_padding\x18\x0f \x01(\r\x12\x1b\n\x13packet_padding_rate\x18\x10 \x01(\x01\x12\x15\n\rbytes_padding\x18\x11 \x01(\x04\x12\x1c\n\x14header_bytes_padding\x18) \x01(\x04\x12\x17\n\x0f\x62itrate_padding\x18\x12 \x01(\x01\x12\x1c\n\x14packets_out_of_order\x18\x13 \x01(\r\x12\x0e\n\x06\x66rames\x18\x14 \x01(\r\x12\x12\n\nframe_rate\x18\x15 \x01(\x01\x12\x16\n\x0ejitter_current\x18\x16 \x01(\x01\x12\x12\n\njitter_max\x18\x17 \x01(\x01\x12:\n\rgap_histogram\x18\x18 \x03(\x0b\x32#.livekit.RTPStats.GapHistogramEntry\x12\r\n\x05nacks\x18\x19 \x01(\r\x12\x11\n\tnack_acks\x18% \x01(\r\x12\x13\n\x0bnack_misses\x18\x1a \x01(\r\x12\x15\n\rnack_repeated\x18& \x01(\r\x12\x0c\n\x04plis\x18\x1b \x01(\r\x12,\n\x08last_pli\x18\x1c \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x0c\n\x04\x66irs\x18\x1d \x01(\r\x12,\n\x08last_fir\x18\x1e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x13\n\x0brtt_current\x18\x1f \x01(\r\x12\x0f\n\x07rtt_max\x18 \x01(\r\x12\x12\n\nkey_frames\x18! \x01(\r\x12\x32\n\x0elast_key_frame\x18\" \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x17\n\x0flayer_lock_plis\x18# \x01(\r\x12\x37\n\x13last_layer_lock_pli\x18$ \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\'\n\x0cpacket_drift\x18, \x01(\x0b\x32\x11.livekit.RTPDrift\x12+\n\x10ntp_report_drift\x18- \x01(\x0b\x32\x11.livekit.RTPDrift\x12/\n\x14rebased_report_drift\x18. \x01(\x0b\x32\x11.livekit.RTPDrift\x12\x30\n\x15received_report_drift\x18/ \x01(\x0b\x32\x11.livekit.RTPDrift\x1a\x33\n\x11GapHistogramEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\r:\x02\x38\x01\"\xa2\x01\n\x15RTCPSenderReportState\x12\x15\n\rrtp_timestamp\x18\x01 \x01(\r\x12\x19\n\x11rtp_timestamp_ext\x18\x02 \x01(\x04\x12\x15\n\rntp_timestamp\x18\x03 \x01(\x04\x12\n\n\x02\x61t\x18\x04 \x01(\x03\x12\x13\n\x0b\x61t_adjusted\x18\x05 \x01(\x03\x12\x0f\n\x07packets\x18\x06 \x01(\r\x12\x0e\n\x06octets\x18\x07 \x01(\x04\"\xc9\x02\n\x11RTPForwarderState\x12\x0f\n\x07started\x18\x01 \x01(\x08\x12\x1f\n\x17reference_layer_spatial\x18\x02 \x01(\x05\x12\x16\n\x0epre_start_time\x18\x03 \x01(\x03\x12\x1b\n\x13\x65xt_first_timestamp\x18\x04 \x01(\x04\x12$\n\x1c\x64ummy_start_timestamp_offset\x18\x05 \x01(\x04\x12+\n\nrtp_munger\x18\x06 \x01(\x0b\x32\x17.livekit.RTPMungerState\x12-\n\nvp8_munger\x18\x07 \x01(\x0b\x32\x17.livekit.VP8MungerStateH\x00\x12;\n\x13sender_report_state\x18\x08 \x03(\x0b\x32\x1e.livekit.RTCPSenderReportStateB\x0e\n\x0c\x63odec_munger\"\xcb\x01\n\x0eRTPMungerState\x12 \n\x18\x65xt_last_sequence_number\x18\x01 \x01(\x04\x12\'\n\x1f\x65xt_second_last_sequence_number\x18\x02 \x01(\x04\x12\x1a\n\x12\x65xt_last_timestamp\x18\x03 \x01(\x04\x12!\n\x19\x65xt_second_last_timestamp\x18\x04 \x01(\x04\x12\x13\n\x0blast_marker\x18\x05 \x01(\x08\x12\x1a\n\x12second_last_marker\x18\x06 \x01(\x08\"\xcd\x01\n\x0eVP8MungerState\x12\x30\n\x13\x65xt_last_picture_id\x18\x01 \x01(\x05\x42\x13\xbaP\x10\x65xtLastPictureID\x12\x17\n\x0fpicture_id_used\x18\x02 \x01(\x08\x12\x18\n\x10last_tl0_pic_idx\x18\x03 \x01(\r\x12\x18\n\x10tl0_pic_idx_used\x18\x04 \x01(\x08\x12\x10\n\x08tid_used\x18\x05 \x01(\x08\x12\x14\n\x0clast_key_idx\x18\x06 \x01(\r\x12\x14\n\x0ckey_idx_used\x18\x07 \x01(\x08\"1\n\x0cTimedVersion\x12\x12\n\nunix_micro\x18\x01 \x01(\x03\x12\r\n\x05ticks\x18\x02 \x01(\x05\"\xa0\t\n\nDataStream\x1a\xb8\x01\n\nTextHeader\x12\x39\n\x0eoperation_type\x18\x01 \x01(\x0e\x32!.livekit.DataStream.OperationType\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12.\n\x12reply_to_stream_id\x18\x03 \x01(\tB\x12\xbaP\x0freplyToStreamID\x12\x1b\n\x13\x61ttached_stream_ids\x18\x04 \x03(\t\x12\x11\n\tgenerated\x18\x05 \x01(\x08\x1a\x1a\n\nByteHeader\x12\x0c\n\x04name\x18\x01 \x01(\t\x1a\x9d\x04\n\x06Header\x12\x1e\n\tstream_id\x18\x01 \x01(\tB\x0b\xbaP\x08streamID\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\r\n\x05topic\x18\x03 \x01(\t\x12\x11\n\tmime_type\x18\x04 \x01(\t\x12\x19\n\x0ctotal_length\x18\x05 \x01(\x04H\x01\x88\x01\x01\x12\x35\n\x0f\x65ncryption_type\x18\x07 \x01(\x0e\x32\x18.livekit.Encryption.TypeB\x02\x18\x01\x12>\n\nattributes\x18\x08 \x03(\x0b\x32*.livekit.DataStream.Header.AttributesEntry\x12\x35\n\x0btext_header\x18\t \x01(\x0b\x32\x1e.livekit.DataStream.TextHeaderH\x00\x12\x35\n\x0b\x62yte_header\x18\n \x01(\x0b\x32\x1e.livekit.DataStream.ByteHeaderH\x00\x12\x1b\n\x0einline_content\x18\x0b \x01(\x0cH\x02\x88\x01\x01\x12\x38\n\x0b\x63ompression\x18\x0c \x01(\x0e\x32#.livekit.DataStream.CompressionType\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x10\n\x0e\x63ontent_headerB\x0f\n\r_total_lengthB\x11\n\x0f_inline_content\x1az\n\x05\x43hunk\x12\x1e\n\tstream_id\x18\x01 \x01(\tB\x0b\xbaP\x08streamID\x12\x13\n\x0b\x63hunk_index\x18\x02 \x01(\x04\x12\x0f\n\x07\x63ontent\x18\x03 \x01(\x0c\x12\x0f\n\x07version\x18\x04 \x01(\x05\x12\x13\n\x02iv\x18\x05 \x01(\x0c\x42\x02\x18\x01H\x00\x88\x01\x01\x42\x05\n\x03_iv\x1a\xad\x01\n\x07Trailer\x12\x1e\n\tstream_id\x18\x01 \x01(\tB\x0b\xbaP\x08streamID\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12?\n\nattributes\x18\x03 \x03(\x0b\x32+.livekit.DataStream.Trailer.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"A\n\rOperationType\x12\n\n\x06\x43REATE\x10\x00\x12\n\n\x06UPDATE\x10\x01\x12\n\n\x06\x44\x45LETE\x10\x02\x12\x0c\n\x08REACTION\x10\x03\",\n\x0f\x43ompressionType\x12\x08\n\x04NONE\x10\x00\x12\x0f\n\x0b\x44\x45\x46LATE_RAW\x10\x01\">\n\x0c\x46ilterParams\x12\x16\n\x0einclude_events\x18\x01 \x03(\t\x12\x16\n\x0e\x65xclude_events\x18\x02 \x03(\t\"_\n\rWebhookConfig\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\x13\n\x0bsigning_key\x18\x02 \x01(\t\x12,\n\rfilter_params\x18\x03 \x01(\x0b\x32\x15.livekit.FilterParams\"6\n\x14SubscribedAudioCodec\x12\r\n\x05\x63odec\x18\x01 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08*;\n\nAudioCodec\x12\x0e\n\nDEFAULT_AC\x10\x00\x12\x08\n\x04OPUS\x10\x01\x12\x07\n\x03\x41\x41\x43\x10\x02\x12\n\n\x06\x41\x43_MP3\x10\x03*V\n\nVideoCodec\x12\x0e\n\nDEFAULT_VC\x10\x00\x12\x11\n\rH264_BASELINE\x10\x01\x12\r\n\tH264_MAIN\x10\x02\x12\r\n\tH264_HIGH\x10\x03\x12\x07\n\x03VP8\x10\x04*)\n\nImageCodec\x12\x0e\n\nIC_DEFAULT\x10\x00\x12\x0b\n\x07IC_JPEG\x10\x01*I\n\x11\x42\x61\x63kupCodecPolicy\x12\x15\n\x11PREFER_REGRESSION\x10\x00\x12\r\n\tSIMULCAST\x10\x01\x12\x0e\n\nREGRESSION\x10\x02*+\n\tTrackType\x12\t\n\x05\x41UDIO\x10\x00\x12\t\n\x05VIDEO\x10\x01\x12\x08\n\x04\x44\x41TA\x10\x02*`\n\x0bTrackSource\x12\x0b\n\x07UNKNOWN\x10\x00\x12\n\n\x06\x43\x41MERA\x10\x01\x12\x0e\n\nMICROPHONE\x10\x02\x12\x10\n\x0cSCREEN_SHARE\x10\x03\x12\x16\n\x12SCREEN_SHARE_AUDIO\x10\x04*B\n\x14\x44\x61taTrackExtensionID\x12\x10\n\x0c\x44TEI_INVALID\x10\x00\x12\x18\n\x14\x44TEI_PARTICIPANT_SID\x10\x01*6\n\x0cVideoQuality\x12\x07\n\x03LOW\x10\x00\x12\n\n\x06MEDIUM\x10\x01\x12\x08\n\x04HIGH\x10\x02\x12\x07\n\x03OFF\x10\x03*@\n\x11\x43onnectionQuality\x12\x08\n\x04POOR\x10\x00\x12\x08\n\x04GOOD\x10\x01\x12\r\n\tEXCELLENT\x10\x02\x12\x08\n\x04LOST\x10\x03*;\n\x13\x43lientConfigSetting\x12\t\n\x05UNSET\x10\x00\x12\x0c\n\x08\x44ISABLED\x10\x01\x12\x0b\n\x07\x45NABLED\x10\x02*\xe8\x02\n\x10\x44isconnectReason\x12\x12\n\x0eUNKNOWN_REASON\x10\x00\x12\x14\n\x10\x43LIENT_INITIATED\x10\x01\x12\x16\n\x12\x44UPLICATE_IDENTITY\x10\x02\x12\x13\n\x0fSERVER_SHUTDOWN\x10\x03\x12\x17\n\x13PARTICIPANT_REMOVED\x10\x04\x12\x10\n\x0cROOM_DELETED\x10\x05\x12\x12\n\x0eSTATE_MISMATCH\x10\x06\x12\x10\n\x0cJOIN_FAILURE\x10\x07\x12\r\n\tMIGRATION\x10\x08\x12\x10\n\x0cSIGNAL_CLOSE\x10\t\x12\x0f\n\x0bROOM_CLOSED\x10\n\x12\x14\n\x10USER_UNAVAILABLE\x10\x0b\x12\x11\n\rUSER_REJECTED\x10\x0c\x12\x15\n\x11SIP_TRUNK_FAILURE\x10\r\x12\x16\n\x12\x43ONNECTION_TIMEOUT\x10\x0e\x12\x11\n\rMEDIA_FAILURE\x10\x0f\x12\x0f\n\x0b\x41GENT_ERROR\x10\x10*\x89\x01\n\x0fReconnectReason\x12\x0e\n\nRR_UNKNOWN\x10\x00\x12\x1a\n\x16RR_SIGNAL_DISCONNECTED\x10\x01\x12\x17\n\x13RR_PUBLISHER_FAILED\x10\x02\x12\x18\n\x14RR_SUBSCRIBER_FAILED\x10\x03\x12\x17\n\x13RR_SWITCH_CANDIDATE\x10\x04*T\n\x11SubscriptionError\x12\x0e\n\nSE_UNKNOWN\x10\x00\x12\x18\n\x14SE_CODEC_UNSUPPORTED\x10\x01\x12\x15\n\x11SE_TRACK_NOTFOUND\x10\x02*\xbd\x01\n\x11\x41udioTrackFeature\x12\r\n\tTF_STEREO\x10\x00\x12\r\n\tTF_NO_DTX\x10\x01\x12\x18\n\x14TF_AUTO_GAIN_CONTROL\x10\x02\x12\x18\n\x14TF_ECHO_CANCELLATION\x10\x03\x12\x18\n\x14TF_NOISE_SUPPRESSION\x10\x04\x12\"\n\x1eTF_ENHANCED_NOISE_CANCELLATION\x10\x05\x12\x18\n\x14TF_PRECONNECT_BUFFER\x10\x06*S\n\x14PacketTrailerFeature\x12\x16\n\x12PTF_USER_TIMESTAMP\x10\x00\x12\x10\n\x0cPTF_FRAME_ID\x10\x01\x12\x11\n\rPTF_USER_DATA\x10\x02\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'models', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_PARTICIPANTPERMISSION'].fields_by_name['recorder']._options = None + _globals['_PAGINATION'].fields_by_name['after_id']._loaded_options = None + _globals['_PAGINATION'].fields_by_name['after_id']._serialized_options = b'\272P\007afterID' + _globals['_ROOM'].fields_by_name['metadata']._loaded_options = None + _globals['_ROOM'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_PARTICIPANTPERMISSION'].fields_by_name['recorder']._loaded_options = None _globals['_PARTICIPANTPERMISSION'].fields_by_name['recorder']._serialized_options = b'\030\001' - _globals['_PARTICIPANTPERMISSION'].fields_by_name['agent']._options = None + _globals['_PARTICIPANTPERMISSION'].fields_by_name['agent']._loaded_options = None _globals['_PARTICIPANTPERMISSION'].fields_by_name['agent']._serialized_options = b'\030\001' - _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._options = None + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._loaded_options = None _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_DATAPACKET'].fields_by_name['kind']._options = None + _globals['_PARTICIPANTINFO'].fields_by_name['metadata']._loaded_options = None + _globals['_PARTICIPANTINFO'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_PARTICIPANTINFO'].fields_by_name['name']._loaded_options = None + _globals['_PARTICIPANTINFO'].fields_by_name['name']._serialized_options = b'\300P\001' + _globals['_PARTICIPANTINFO'].fields_by_name['attributes']._loaded_options = None + _globals['_PARTICIPANTINFO'].fields_by_name['attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_TRACKINFO'].fields_by_name['name']._loaded_options = None + _globals['_TRACKINFO'].fields_by_name['name']._serialized_options = b'\300P\001' + _globals['_TRACKINFO'].fields_by_name['simulcast']._loaded_options = None + _globals['_TRACKINFO'].fields_by_name['simulcast']._serialized_options = b'\030\001' + _globals['_TRACKINFO'].fields_by_name['disable_dtx']._loaded_options = None + _globals['_TRACKINFO'].fields_by_name['disable_dtx']._serialized_options = b'\030\001' + _globals['_TRACKINFO'].fields_by_name['layers']._loaded_options = None + _globals['_TRACKINFO'].fields_by_name['layers']._serialized_options = b'\030\001' + _globals['_TRACKINFO'].fields_by_name['stereo']._loaded_options = None + _globals['_TRACKINFO'].fields_by_name['stereo']._serialized_options = b'\030\001' + _globals['_DATABLOBKEY'].fields_by_name['schema_id']._loaded_options = None + _globals['_DATABLOBKEY'].fields_by_name['schema_id']._serialized_options = b'\272P\010schemaID' + _globals['_DATABLOB'].fields_by_name['contents']._loaded_options = None + _globals['_DATABLOB'].fields_by_name['contents']._serialized_options = b'\262P\036\300P\001' + _globals['_DATAPACKET'].fields_by_name['kind']._loaded_options = None _globals['_DATAPACKET'].fields_by_name['kind']._serialized_options = b'\030\001' - _globals['_DATAPACKET'].fields_by_name['speaker']._options = None + _globals['_DATAPACKET'].fields_by_name['speaker']._loaded_options = None _globals['_DATAPACKET'].fields_by_name['speaker']._serialized_options = b'\030\001' - _globals['_USERPACKET'].fields_by_name['participant_sid']._options = None + _globals['_ACTIVESPEAKERUPDATE']._loaded_options = None + _globals['_ACTIVESPEAKERUPDATE']._serialized_options = b'\030\001' + _globals['_USERPACKET'].fields_by_name['participant_sid']._loaded_options = None _globals['_USERPACKET'].fields_by_name['participant_sid']._serialized_options = b'\030\001' - _globals['_USERPACKET'].fields_by_name['participant_identity']._options = None + _globals['_USERPACKET'].fields_by_name['participant_identity']._loaded_options = None _globals['_USERPACKET'].fields_by_name['participant_identity']._serialized_options = b'\030\001' - _globals['_USERPACKET'].fields_by_name['destination_sids']._options = None + _globals['_USERPACKET'].fields_by_name['destination_sids']._loaded_options = None _globals['_USERPACKET'].fields_by_name['destination_sids']._serialized_options = b'\030\001' - _globals['_USERPACKET'].fields_by_name['destination_identities']._options = None + _globals['_USERPACKET'].fields_by_name['destination_identities']._loaded_options = None _globals['_USERPACKET'].fields_by_name['destination_identities']._serialized_options = b'\030\001' - _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._options = None + _globals['_TRANSCRIPTION'].fields_by_name['track_id']._loaded_options = None + _globals['_TRANSCRIPTION'].fields_by_name['track_id']._serialized_options = b'\272P\007trackID' + _globals['_RPCACK'].fields_by_name['request_id']._loaded_options = None + _globals['_RPCACK'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_RPCRESPONSE'].fields_by_name['request_id']._loaded_options = None + _globals['_RPCRESPONSE'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_SERVERINFO'].fields_by_name['node_id']._loaded_options = None + _globals['_SERVERINFO'].fields_by_name['node_id']._serialized_options = b'\272P\006nodeID' + _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._loaded_options = None _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._serialized_options = b'8\001' - _globals['_AUDIOCODEC']._serialized_start=5814 - _globals['_AUDIOCODEC']._serialized_end=5861 - _globals['_VIDEOCODEC']._serialized_start=5863 - _globals['_VIDEOCODEC']._serialized_end=5949 - _globals['_IMAGECODEC']._serialized_start=5951 - _globals['_IMAGECODEC']._serialized_end=5992 - _globals['_TRACKTYPE']._serialized_start=5994 - _globals['_TRACKTYPE']._serialized_end=6037 - _globals['_TRACKSOURCE']._serialized_start=6039 - _globals['_TRACKSOURCE']._serialized_end=6135 - _globals['_VIDEOQUALITY']._serialized_start=6137 - _globals['_VIDEOQUALITY']._serialized_end=6191 - _globals['_CONNECTIONQUALITY']._serialized_start=6193 - _globals['_CONNECTIONQUALITY']._serialized_end=6257 - _globals['_CLIENTCONFIGSETTING']._serialized_start=6259 - _globals['_CLIENTCONFIGSETTING']._serialized_end=6318 - _globals['_DISCONNECTREASON']._serialized_start=6321 - _globals['_DISCONNECTREASON']._serialized_end=6540 - _globals['_RECONNECTREASON']._serialized_start=6543 - _globals['_RECONNECTREASON']._serialized_end=6680 - _globals['_SUBSCRIPTIONERROR']._serialized_start=6682 - _globals['_SUBSCRIPTIONERROR']._serialized_end=6766 - _globals['_AUDIOTRACKFEATURE']._serialized_start=6769 - _globals['_AUDIOTRACKFEATURE']._serialized_end=6932 - _globals['_ROOM']._serialized_start=67 - _globals['_ROOM']._serialized_end=396 - _globals['_CODEC']._serialized_start=398 - _globals['_CODEC']._serialized_end=438 - _globals['_PLAYOUTDELAY']._serialized_start=440 - _globals['_PLAYOUTDELAY']._serialized_end=497 - _globals['_PARTICIPANTPERMISSION']._serialized_start=500 - _globals['_PARTICIPANTPERMISSION']._serialized_end=730 - _globals['_PARTICIPANTINFO']._serialized_start=733 - _globals['_PARTICIPANTINFO']._serialized_end=1311 - _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_start=1131 - _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_end=1180 - _globals['_PARTICIPANTINFO_STATE']._serialized_start=1182 - _globals['_PARTICIPANTINFO_STATE']._serialized_end=1244 - _globals['_PARTICIPANTINFO_KIND']._serialized_start=1246 - _globals['_PARTICIPANTINFO_KIND']._serialized_end=1311 - _globals['_ENCRYPTION']._serialized_start=1313 - _globals['_ENCRYPTION']._serialized_end=1364 - _globals['_ENCRYPTION_TYPE']._serialized_start=1327 - _globals['_ENCRYPTION_TYPE']._serialized_end=1364 - _globals['_SIMULCASTCODECINFO']._serialized_start=1366 - _globals['_SIMULCASTCODECINFO']._serialized_end=1468 - _globals['_TRACKINFO']._serialized_start=1471 - _globals['_TRACKINFO']._serialized_end=1972 - _globals['_VIDEOLAYER']._serialized_start=1974 - _globals['_VIDEOLAYER']._serialized_end=2088 - _globals['_DATAPACKET']._serialized_start=2091 - _globals['_DATAPACKET']._serialized_end=2428 - _globals['_DATAPACKET_KIND']._serialized_start=2388 - _globals['_DATAPACKET_KIND']._serialized_end=2419 - _globals['_ACTIVESPEAKERUPDATE']._serialized_start=2430 - _globals['_ACTIVESPEAKERUPDATE']._serialized_end=2491 - _globals['_SPEAKERINFO']._serialized_start=2493 - _globals['_SPEAKERINFO']._serialized_end=2550 - _globals['_USERPACKET']._serialized_start=2553 - _globals['_USERPACKET']._serialized_end=2841 - _globals['_SIPDTMF']._serialized_start=2843 - _globals['_SIPDTMF']._serialized_end=2881 - _globals['_TRANSCRIPTION']._serialized_start=2883 - _globals['_TRANSCRIPTION']._serialized_end=3007 - _globals['_TRANSCRIPTIONSEGMENT']._serialized_start=3009 - _globals['_TRANSCRIPTIONSEGMENT']._serialized_end=3128 - _globals['_PARTICIPANTTRACKS']._serialized_start=3130 - _globals['_PARTICIPANTTRACKS']._serialized_end=3194 - _globals['_SERVERINFO']._serialized_start=3197 - _globals['_SERVERINFO']._serialized_end=3403 - _globals['_SERVERINFO_EDITION']._serialized_start=3369 - _globals['_SERVERINFO_EDITION']._serialized_end=3403 - _globals['_CLIENTINFO']._serialized_start=3406 - _globals['_CLIENTINFO']._serialized_end=3755 - _globals['_CLIENTINFO_SDK']._serialized_start=3624 - _globals['_CLIENTINFO_SDK']._serialized_end=3755 - _globals['_CLIENTCONFIGURATION']._serialized_start=3758 - _globals['_CLIENTCONFIGURATION']._serialized_end=4026 - _globals['_VIDEOCONFIGURATION']._serialized_start=4028 - _globals['_VIDEOCONFIGURATION']._serialized_end=4104 - _globals['_DISABLEDCODECS']._serialized_start=4106 - _globals['_DISABLEDCODECS']._serialized_end=4187 - _globals['_RTPDRIFT']._serialized_start=4190 - _globals['_RTPDRIFT']._serialized_end=4446 - _globals['_RTPSTATS']._serialized_start=4449 - _globals['_RTPSTATS']._serialized_end=5761 - _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._serialized_start=5710 - _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._serialized_end=5761 - _globals['_TIMEDVERSION']._serialized_start=5763 - _globals['_TIMEDVERSION']._serialized_end=5812 + _globals['_VP8MUNGERSTATE'].fields_by_name['ext_last_picture_id']._loaded_options = None + _globals['_VP8MUNGERSTATE'].fields_by_name['ext_last_picture_id']._serialized_options = b'\272P\020extLastPictureID' + _globals['_DATASTREAM_TEXTHEADER'].fields_by_name['reply_to_stream_id']._loaded_options = None + _globals['_DATASTREAM_TEXTHEADER'].fields_by_name['reply_to_stream_id']._serialized_options = b'\272P\017replyToStreamID' + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._loaded_options = None + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DATASTREAM_HEADER'].fields_by_name['stream_id']._loaded_options = None + _globals['_DATASTREAM_HEADER'].fields_by_name['stream_id']._serialized_options = b'\272P\010streamID' + _globals['_DATASTREAM_HEADER'].fields_by_name['encryption_type']._loaded_options = None + _globals['_DATASTREAM_HEADER'].fields_by_name['encryption_type']._serialized_options = b'\030\001' + _globals['_DATASTREAM_CHUNK'].fields_by_name['stream_id']._loaded_options = None + _globals['_DATASTREAM_CHUNK'].fields_by_name['stream_id']._serialized_options = b'\272P\010streamID' + _globals['_DATASTREAM_CHUNK'].fields_by_name['iv']._loaded_options = None + _globals['_DATASTREAM_CHUNK'].fields_by_name['iv']._serialized_options = b'\030\001' + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._loaded_options = None + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DATASTREAM_TRAILER'].fields_by_name['stream_id']._loaded_options = None + _globals['_DATASTREAM_TRAILER'].fields_by_name['stream_id']._serialized_options = b'\272P\010streamID' + _globals['_AUDIOCODEC']._serialized_start=12925 + _globals['_AUDIOCODEC']._serialized_end=12984 + _globals['_VIDEOCODEC']._serialized_start=12986 + _globals['_VIDEOCODEC']._serialized_end=13072 + _globals['_IMAGECODEC']._serialized_start=13074 + _globals['_IMAGECODEC']._serialized_end=13115 + _globals['_BACKUPCODECPOLICY']._serialized_start=13117 + _globals['_BACKUPCODECPOLICY']._serialized_end=13190 + _globals['_TRACKTYPE']._serialized_start=13192 + _globals['_TRACKTYPE']._serialized_end=13235 + _globals['_TRACKSOURCE']._serialized_start=13237 + _globals['_TRACKSOURCE']._serialized_end=13333 + _globals['_DATATRACKEXTENSIONID']._serialized_start=13335 + _globals['_DATATRACKEXTENSIONID']._serialized_end=13401 + _globals['_VIDEOQUALITY']._serialized_start=13403 + _globals['_VIDEOQUALITY']._serialized_end=13457 + _globals['_CONNECTIONQUALITY']._serialized_start=13459 + _globals['_CONNECTIONQUALITY']._serialized_end=13523 + _globals['_CLIENTCONFIGSETTING']._serialized_start=13525 + _globals['_CLIENTCONFIGSETTING']._serialized_end=13584 + _globals['_DISCONNECTREASON']._serialized_start=13587 + _globals['_DISCONNECTREASON']._serialized_end=13947 + _globals['_RECONNECTREASON']._serialized_start=13950 + _globals['_RECONNECTREASON']._serialized_end=14087 + _globals['_SUBSCRIPTIONERROR']._serialized_start=14089 + _globals['_SUBSCRIPTIONERROR']._serialized_end=14173 + _globals['_AUDIOTRACKFEATURE']._serialized_start=14176 + _globals['_AUDIOTRACKFEATURE']._serialized_end=14365 + _globals['_PACKETTRAILERFEATURE']._serialized_start=14367 + _globals['_PACKETTRAILERFEATURE']._serialized_end=14450 + _globals['_PAGINATION']._serialized_start=111 + _globals['_PAGINATION']._serialized_end=168 + _globals['_TOKENPAGINATION']._serialized_start=170 + _globals['_TOKENPAGINATION']._serialized_end=202 + _globals['_LISTUPDATE']._serialized_start=204 + _globals['_LISTUPDATE']._serialized_end=273 + _globals['_ROOM']._serialized_start=276 + _globals['_ROOM']._serialized_end=669 + _globals['_CODEC']._serialized_start=671 + _globals['_CODEC']._serialized_end=711 + _globals['_PLAYOUTDELAY']._serialized_start=713 + _globals['_PLAYOUTDELAY']._serialized_end=770 + _globals['_PARTICIPANTPERMISSION']._serialized_start=773 + _globals['_PARTICIPANTPERMISSION']._serialized_end=1068 + _globals['_PARTICIPANTINFO']._serialized_start=1071 + _globals['_PARTICIPANTINFO']._serialized_end=2141 + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_start=1809 + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_end=1858 + _globals['_PARTICIPANTINFO_STATE']._serialized_start=1860 + _globals['_PARTICIPANTINFO_STATE']._serialized_end=1922 + _globals['_PARTICIPANTINFO_KIND']._serialized_start=1924 + _globals['_PARTICIPANTINFO_KIND']._serialized_end=2016 + _globals['_PARTICIPANTINFO_KINDDETAIL']._serialized_start=2018 + _globals['_PARTICIPANTINFO_KINDDETAIL']._serialized_end=2141 + _globals['_ENCRYPTION']._serialized_start=2143 + _globals['_ENCRYPTION']._serialized_end=2194 + _globals['_ENCRYPTION_TYPE']._serialized_start=2157 + _globals['_ENCRYPTION_TYPE']._serialized_end=2194 + _globals['_SIMULCASTCODECINFO']._serialized_start=2197 + _globals['_SIMULCASTCODECINFO']._serialized_end=2368 + _globals['_TRACKINFO']._serialized_start=2371 + _globals['_TRACKINFO']._serialized_end=3014 + _globals['_DATATRACKINFO']._serialized_start=3017 + _globals['_DATATRACKINFO']._serialized_end=3266 + _globals['_DATATRACKFRAMEENCODING']._serialized_start=3269 + _globals['_DATATRACKFRAMEENCODING']._serialized_end=3732 + _globals['_DATATRACKFRAMEENCODING_WELLKNOWNFRAMEENCODING']._serialized_start=3392 + _globals['_DATATRACKFRAMEENCODING_WELLKNOWNFRAMEENCODING']._serialized_end=3723 + _globals['_DATATRACKSCHEMAENCODING']._serialized_start=3735 + _globals['_DATATRACKSCHEMAENCODING']._serialized_end=4230 + _globals['_DATATRACKSCHEMAENCODING_WELLKNOWNSCHEMAENCODING']._serialized_start=3861 + _globals['_DATATRACKSCHEMAENCODING_WELLKNOWNSCHEMAENCODING']._serialized_end=4221 + _globals['_DATATRACKSCHEMAID']._serialized_start=4232 + _globals['_DATATRACKSCHEMAID']._serialized_end=4317 + _globals['_DATATRACKEXTENSIONPARTICIPANTSID']._serialized_start=4319 + _globals['_DATATRACKEXTENSIONPARTICIPANTSID']._serialized_end=4421 + _globals['_DATATRACKSUBSCRIPTIONOPTIONS']._serialized_start=4423 + _globals['_DATATRACKSUBSCRIPTIONOPTIONS']._serialized_end=4493 + _globals['_DATABLOBKEY']._serialized_start=4495 + _globals['_DATABLOBKEY']._serialized_end=4596 + _globals['_DATABLOB']._serialized_start=4598 + _globals['_DATABLOB']._serialized_end=4699 + _globals['_VIDEOLAYER']._serialized_start=4702 + _globals['_VIDEOLAYER']._serialized_end=5026 + _globals['_VIDEOLAYER_MODE']._serialized_start=4876 + _globals['_VIDEOLAYER_MODE']._serialized_end=5026 + _globals['_DATAPACKET']._serialized_start=5029 + _globals['_DATAPACKET']._serialized_end=5836 + _globals['_DATAPACKET_KIND']._serialized_start=5796 + _globals['_DATAPACKET_KIND']._serialized_end=5827 + _globals['_ENCRYPTEDPACKET']._serialized_start=5838 + _globals['_ENCRYPTEDPACKET']._serialized_end=5962 + _globals['_ENCRYPTEDPACKETPAYLOAD']._serialized_start=5965 + _globals['_ENCRYPTEDPACKETPAYLOAD']._serialized_end=6366 + _globals['_ACTIVESPEAKERUPDATE']._serialized_start=6368 + _globals['_ACTIVESPEAKERUPDATE']._serialized_end=6433 + _globals['_SPEAKERINFO']._serialized_start=6435 + _globals['_SPEAKERINFO']._serialized_end=6492 + _globals['_USERPACKET']._serialized_start=6495 + _globals['_USERPACKET']._serialized_end=6798 + _globals['_SIPDTMF']._serialized_start=6800 + _globals['_SIPDTMF']._serialized_end=6838 + _globals['_TRANSCRIPTION']._serialized_start=6841 + _globals['_TRANSCRIPTION']._serialized_end=6977 + _globals['_TRANSCRIPTIONSEGMENT']._serialized_start=6979 + _globals['_TRANSCRIPTIONSEGMENT']._serialized_end=7098 + _globals['_CHATMESSAGE']._serialized_start=7101 + _globals['_CHATMESSAGE']._serialized_end=7246 + _globals['_RPCREQUEST']._serialized_start=7249 + _globals['_RPCREQUEST']._serialized_end=7380 + _globals['_RPCACK']._serialized_start=7382 + _globals['_RPCACK']._serialized_end=7424 + _globals['_RPCRESPONSE']._serialized_start=7427 + _globals['_RPCRESPONSE']._serialized_end=7568 + _globals['_RPCERROR']._serialized_start=7570 + _globals['_RPCERROR']._serialized_end=7625 + _globals['_PARTICIPANTTRACKS']._serialized_start=7627 + _globals['_PARTICIPANTTRACKS']._serialized_end=7691 + _globals['_SERVERINFO']._serialized_start=7694 + _globals['_SERVERINFO']._serialized_end=7911 + _globals['_SERVERINFO_EDITION']._serialized_start=7877 + _globals['_SERVERINFO_EDITION']._serialized_end=7911 + _globals['_CLIENTINFO']._serialized_start=7914 + _globals['_CLIENTINFO']._serialized_end=8497 + _globals['_CLIENTINFO_SDK']._serialized_start=8231 + _globals['_CLIENTINFO_SDK']._serialized_end=8410 + _globals['_CLIENTINFO_CAPABILITY']._serialized_start=8412 + _globals['_CLIENTINFO_CAPABILITY']._serialized_end=8497 + _globals['_CLIENTCONFIGURATION']._serialized_start=8500 + _globals['_CLIENTCONFIGURATION']._serialized_end=8768 + _globals['_VIDEOCONFIGURATION']._serialized_start=8770 + _globals['_VIDEOCONFIGURATION']._serialized_end=8846 + _globals['_DISABLEDCODECS']._serialized_start=8848 + _globals['_DISABLEDCODECS']._serialized_end=8929 + _globals['_RTPDRIFT']._serialized_start=8932 + _globals['_RTPDRIFT']._serialized_end=9188 + _globals['_RTPSTATS']._serialized_start=9191 + _globals['_RTPSTATS']._serialized_end=10557 + _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._serialized_start=10506 + _globals['_RTPSTATS_GAPHISTOGRAMENTRY']._serialized_end=10557 + _globals['_RTCPSENDERREPORTSTATE']._serialized_start=10560 + _globals['_RTCPSENDERREPORTSTATE']._serialized_end=10722 + _globals['_RTPFORWARDERSTATE']._serialized_start=10725 + _globals['_RTPFORWARDERSTATE']._serialized_end=11054 + _globals['_RTPMUNGERSTATE']._serialized_start=11057 + _globals['_RTPMUNGERSTATE']._serialized_end=11260 + _globals['_VP8MUNGERSTATE']._serialized_start=11263 + _globals['_VP8MUNGERSTATE']._serialized_end=11468 + _globals['_TIMEDVERSION']._serialized_start=11470 + _globals['_TIMEDVERSION']._serialized_end=11519 + _globals['_DATASTREAM']._serialized_start=11522 + _globals['_DATASTREAM']._serialized_end=12706 + _globals['_DATASTREAM_TEXTHEADER']._serialized_start=11537 + _globals['_DATASTREAM_TEXTHEADER']._serialized_end=11721 + _globals['_DATASTREAM_BYTEHEADER']._serialized_start=11723 + _globals['_DATASTREAM_BYTEHEADER']._serialized_end=11749 + _globals['_DATASTREAM_HEADER']._serialized_start=11752 + _globals['_DATASTREAM_HEADER']._serialized_end=12293 + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_start=1809 + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_end=1858 + _globals['_DATASTREAM_CHUNK']._serialized_start=12295 + _globals['_DATASTREAM_CHUNK']._serialized_end=12417 + _globals['_DATASTREAM_TRAILER']._serialized_start=12420 + _globals['_DATASTREAM_TRAILER']._serialized_end=12593 + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_start=1809 + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_end=1858 + _globals['_DATASTREAM_OPERATIONTYPE']._serialized_start=12595 + _globals['_DATASTREAM_OPERATIONTYPE']._serialized_end=12660 + _globals['_DATASTREAM_COMPRESSIONTYPE']._serialized_start=12662 + _globals['_DATASTREAM_COMPRESSIONTYPE']._serialized_end=12706 + _globals['_FILTERPARAMS']._serialized_start=12708 + _globals['_FILTERPARAMS']._serialized_end=12770 + _globals['_WEBHOOKCONFIG']._serialized_start=12772 + _globals['_WEBHOOKCONFIG']._serialized_end=12867 + _globals['_SUBSCRIBEDAUDIOCODEC']._serialized_start=12869 + _globals['_SUBSCRIBEDAUDIOCODEC']._serialized_end=12923 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/models.pyi b/livekit-protocol/livekit/protocol/models.pyi index cdef3e6c..b5a35558 100644 --- a/livekit-protocol/livekit/protocol/models.pyi +++ b/livekit-protocol/livekit/protocol/models.pyi @@ -1,4 +1,6 @@ from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from . import metrics as _metrics +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -12,6 +14,7 @@ class AudioCodec(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): DEFAULT_AC: _ClassVar[AudioCodec] OPUS: _ClassVar[AudioCodec] AAC: _ClassVar[AudioCodec] + AC_MP3: _ClassVar[AudioCodec] class VideoCodec(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -26,6 +29,12 @@ class ImageCodec(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): IC_DEFAULT: _ClassVar[ImageCodec] IC_JPEG: _ClassVar[ImageCodec] +class BackupCodecPolicy(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PREFER_REGRESSION: _ClassVar[BackupCodecPolicy] + SIMULCAST: _ClassVar[BackupCodecPolicy] + REGRESSION: _ClassVar[BackupCodecPolicy] + class TrackType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () AUDIO: _ClassVar[TrackType] @@ -40,6 +49,11 @@ class TrackSource(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): SCREEN_SHARE: _ClassVar[TrackSource] SCREEN_SHARE_AUDIO: _ClassVar[TrackSource] +class DataTrackExtensionID(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DTEI_INVALID: _ClassVar[DataTrackExtensionID] + DTEI_PARTICIPANT_SID: _ClassVar[DataTrackExtensionID] + class VideoQuality(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () LOW: _ClassVar[VideoQuality] @@ -72,6 +86,13 @@ class DisconnectReason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): JOIN_FAILURE: _ClassVar[DisconnectReason] MIGRATION: _ClassVar[DisconnectReason] SIGNAL_CLOSE: _ClassVar[DisconnectReason] + ROOM_CLOSED: _ClassVar[DisconnectReason] + USER_UNAVAILABLE: _ClassVar[DisconnectReason] + USER_REJECTED: _ClassVar[DisconnectReason] + SIP_TRUNK_FAILURE: _ClassVar[DisconnectReason] + CONNECTION_TIMEOUT: _ClassVar[DisconnectReason] + MEDIA_FAILURE: _ClassVar[DisconnectReason] + AGENT_ERROR: _ClassVar[DisconnectReason] class ReconnectReason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () @@ -95,9 +116,17 @@ class AudioTrackFeature(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): TF_ECHO_CANCELLATION: _ClassVar[AudioTrackFeature] TF_NOISE_SUPPRESSION: _ClassVar[AudioTrackFeature] TF_ENHANCED_NOISE_CANCELLATION: _ClassVar[AudioTrackFeature] + TF_PRECONNECT_BUFFER: _ClassVar[AudioTrackFeature] + +class PacketTrailerFeature(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PTF_USER_TIMESTAMP: _ClassVar[PacketTrailerFeature] + PTF_FRAME_ID: _ClassVar[PacketTrailerFeature] + PTF_USER_DATA: _ClassVar[PacketTrailerFeature] DEFAULT_AC: AudioCodec OPUS: AudioCodec AAC: AudioCodec +AC_MP3: AudioCodec DEFAULT_VC: VideoCodec H264_BASELINE: VideoCodec H264_MAIN: VideoCodec @@ -105,6 +134,9 @@ H264_HIGH: VideoCodec VP8: VideoCodec IC_DEFAULT: ImageCodec IC_JPEG: ImageCodec +PREFER_REGRESSION: BackupCodecPolicy +SIMULCAST: BackupCodecPolicy +REGRESSION: BackupCodecPolicy AUDIO: TrackType VIDEO: TrackType DATA: TrackType @@ -113,6 +145,8 @@ CAMERA: TrackSource MICROPHONE: TrackSource SCREEN_SHARE: TrackSource SCREEN_SHARE_AUDIO: TrackSource +DTEI_INVALID: DataTrackExtensionID +DTEI_PARTICIPANT_SID: DataTrackExtensionID LOW: VideoQuality MEDIUM: VideoQuality HIGH: VideoQuality @@ -134,6 +168,13 @@ STATE_MISMATCH: DisconnectReason JOIN_FAILURE: DisconnectReason MIGRATION: DisconnectReason SIGNAL_CLOSE: DisconnectReason +ROOM_CLOSED: DisconnectReason +USER_UNAVAILABLE: DisconnectReason +USER_REJECTED: DisconnectReason +SIP_TRUNK_FAILURE: DisconnectReason +CONNECTION_TIMEOUT: DisconnectReason +MEDIA_FAILURE: DisconnectReason +AGENT_ERROR: DisconnectReason RR_UNKNOWN: ReconnectReason RR_SIGNAL_DISCONNECTED: ReconnectReason RR_PUBLISHER_FAILED: ReconnectReason @@ -148,15 +189,46 @@ TF_AUTO_GAIN_CONTROL: AudioTrackFeature TF_ECHO_CANCELLATION: AudioTrackFeature TF_NOISE_SUPPRESSION: AudioTrackFeature TF_ENHANCED_NOISE_CANCELLATION: AudioTrackFeature +TF_PRECONNECT_BUFFER: AudioTrackFeature +PTF_USER_TIMESTAMP: PacketTrailerFeature +PTF_FRAME_ID: PacketTrailerFeature +PTF_USER_DATA: PacketTrailerFeature + +class Pagination(_message.Message): + __slots__ = ("after_id", "limit") + AFTER_ID_FIELD_NUMBER: _ClassVar[int] + LIMIT_FIELD_NUMBER: _ClassVar[int] + after_id: str + limit: int + def __init__(self, after_id: _Optional[str] = ..., limit: _Optional[int] = ...) -> None: ... + +class TokenPagination(_message.Message): + __slots__ = ("token",) + TOKEN_FIELD_NUMBER: _ClassVar[int] + token: str + def __init__(self, token: _Optional[str] = ...) -> None: ... + +class ListUpdate(_message.Message): + __slots__ = ("set", "add", "remove", "clear") + SET_FIELD_NUMBER: _ClassVar[int] + ADD_FIELD_NUMBER: _ClassVar[int] + REMOVE_FIELD_NUMBER: _ClassVar[int] + CLEAR_FIELD_NUMBER: _ClassVar[int] + set: _containers.RepeatedScalarFieldContainer[str] + add: _containers.RepeatedScalarFieldContainer[str] + remove: _containers.RepeatedScalarFieldContainer[str] + clear: bool + def __init__(self, set: _Optional[_Iterable[str]] = ..., add: _Optional[_Iterable[str]] = ..., remove: _Optional[_Iterable[str]] = ..., clear: bool = ...) -> None: ... class Room(_message.Message): - __slots__ = ("sid", "name", "empty_timeout", "departure_timeout", "max_participants", "creation_time", "turn_password", "enabled_codecs", "metadata", "num_participants", "num_publishers", "active_recording", "version") + __slots__ = ("sid", "name", "empty_timeout", "departure_timeout", "max_participants", "creation_time", "creation_time_ms", "turn_password", "enabled_codecs", "metadata", "num_participants", "num_publishers", "active_recording", "version") SID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] EMPTY_TIMEOUT_FIELD_NUMBER: _ClassVar[int] DEPARTURE_TIMEOUT_FIELD_NUMBER: _ClassVar[int] MAX_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] CREATION_TIME_FIELD_NUMBER: _ClassVar[int] + CREATION_TIME_MS_FIELD_NUMBER: _ClassVar[int] TURN_PASSWORD_FIELD_NUMBER: _ClassVar[int] ENABLED_CODECS_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] @@ -170,6 +242,7 @@ class Room(_message.Message): departure_timeout: int max_participants: int creation_time: int + creation_time_ms: int turn_password: str enabled_codecs: _containers.RepeatedCompositeFieldContainer[Codec] metadata: str @@ -177,7 +250,7 @@ class Room(_message.Message): num_publishers: int active_recording: bool version: TimedVersion - def __init__(self, sid: _Optional[str] = ..., name: _Optional[str] = ..., empty_timeout: _Optional[int] = ..., departure_timeout: _Optional[int] = ..., max_participants: _Optional[int] = ..., creation_time: _Optional[int] = ..., turn_password: _Optional[str] = ..., enabled_codecs: _Optional[_Iterable[_Union[Codec, _Mapping]]] = ..., metadata: _Optional[str] = ..., num_participants: _Optional[int] = ..., num_publishers: _Optional[int] = ..., active_recording: bool = ..., version: _Optional[_Union[TimedVersion, _Mapping]] = ...) -> None: ... + def __init__(self, sid: _Optional[str] = ..., name: _Optional[str] = ..., empty_timeout: _Optional[int] = ..., departure_timeout: _Optional[int] = ..., max_participants: _Optional[int] = ..., creation_time: _Optional[int] = ..., creation_time_ms: _Optional[int] = ..., turn_password: _Optional[str] = ..., enabled_codecs: _Optional[_Iterable[_Union[Codec, _Mapping]]] = ..., metadata: _Optional[str] = ..., num_participants: _Optional[int] = ..., num_publishers: _Optional[int] = ..., active_recording: bool = ..., version: _Optional[_Union[TimedVersion, _Mapping]] = ...) -> None: ... class Codec(_message.Message): __slots__ = ("mime", "fmtp_line") @@ -198,7 +271,7 @@ class PlayoutDelay(_message.Message): def __init__(self, enabled: bool = ..., min: _Optional[int] = ..., max: _Optional[int] = ...) -> None: ... class ParticipantPermission(_message.Message): - __slots__ = ("can_subscribe", "can_publish", "can_publish_data", "can_publish_sources", "hidden", "recorder", "can_update_metadata", "agent") + __slots__ = ("can_subscribe", "can_publish", "can_publish_data", "can_publish_sources", "hidden", "recorder", "can_update_metadata", "agent", "can_subscribe_metrics", "can_manage_agent_session") CAN_SUBSCRIBE_FIELD_NUMBER: _ClassVar[int] CAN_PUBLISH_FIELD_NUMBER: _ClassVar[int] CAN_PUBLISH_DATA_FIELD_NUMBER: _ClassVar[int] @@ -207,6 +280,8 @@ class ParticipantPermission(_message.Message): RECORDER_FIELD_NUMBER: _ClassVar[int] CAN_UPDATE_METADATA_FIELD_NUMBER: _ClassVar[int] AGENT_FIELD_NUMBER: _ClassVar[int] + CAN_SUBSCRIBE_METRICS_FIELD_NUMBER: _ClassVar[int] + CAN_MANAGE_AGENT_SESSION_FIELD_NUMBER: _ClassVar[int] can_subscribe: bool can_publish: bool can_publish_data: bool @@ -215,10 +290,12 @@ class ParticipantPermission(_message.Message): recorder: bool can_update_metadata: bool agent: bool - def __init__(self, can_subscribe: bool = ..., can_publish: bool = ..., can_publish_data: bool = ..., can_publish_sources: _Optional[_Iterable[_Union[TrackSource, str]]] = ..., hidden: bool = ..., recorder: bool = ..., can_update_metadata: bool = ..., agent: bool = ...) -> None: ... + can_subscribe_metrics: bool + can_manage_agent_session: bool + def __init__(self, can_subscribe: bool = ..., can_publish: bool = ..., can_publish_data: bool = ..., can_publish_sources: _Optional[_Iterable[_Union[TrackSource, str]]] = ..., hidden: bool = ..., recorder: bool = ..., can_update_metadata: bool = ..., agent: bool = ..., can_subscribe_metrics: bool = ..., can_manage_agent_session: bool = ...) -> None: ... class ParticipantInfo(_message.Message): - __slots__ = ("sid", "identity", "state", "tracks", "metadata", "joined_at", "name", "version", "permission", "region", "is_publisher", "kind", "attributes") + __slots__ = ("sid", "identity", "state", "tracks", "metadata", "joined_at", "joined_at_ms", "name", "version", "permission", "region", "is_publisher", "kind", "attributes", "disconnect_reason", "kind_details", "data_tracks", "client_protocol", "capabilities") class State(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () JOINING: _ClassVar[ParticipantInfo.State] @@ -236,11 +313,29 @@ class ParticipantInfo(_message.Message): EGRESS: _ClassVar[ParticipantInfo.Kind] SIP: _ClassVar[ParticipantInfo.Kind] AGENT: _ClassVar[ParticipantInfo.Kind] + CONNECTOR: _ClassVar[ParticipantInfo.Kind] + BRIDGE: _ClassVar[ParticipantInfo.Kind] STANDARD: ParticipantInfo.Kind INGRESS: ParticipantInfo.Kind EGRESS: ParticipantInfo.Kind SIP: ParticipantInfo.Kind AGENT: ParticipantInfo.Kind + CONNECTOR: ParticipantInfo.Kind + BRIDGE: ParticipantInfo.Kind + class KindDetail(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CLOUD_AGENT: _ClassVar[ParticipantInfo.KindDetail] + FORWARDED: _ClassVar[ParticipantInfo.KindDetail] + CONNECTOR_WHATSAPP: _ClassVar[ParticipantInfo.KindDetail] + CONNECTOR_TWILIO: _ClassVar[ParticipantInfo.KindDetail] + BRIDGE_RTSP: _ClassVar[ParticipantInfo.KindDetail] + SIMULATION: _ClassVar[ParticipantInfo.KindDetail] + CLOUD_AGENT: ParticipantInfo.KindDetail + FORWARDED: ParticipantInfo.KindDetail + CONNECTOR_WHATSAPP: ParticipantInfo.KindDetail + CONNECTOR_TWILIO: ParticipantInfo.KindDetail + BRIDGE_RTSP: ParticipantInfo.KindDetail + SIMULATION: ParticipantInfo.KindDetail class AttributesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -254,6 +349,7 @@ class ParticipantInfo(_message.Message): TRACKS_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] JOINED_AT_FIELD_NUMBER: _ClassVar[int] + JOINED_AT_MS_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] PERMISSION_FIELD_NUMBER: _ClassVar[int] @@ -261,12 +357,18 @@ class ParticipantInfo(_message.Message): IS_PUBLISHER_FIELD_NUMBER: _ClassVar[int] KIND_FIELD_NUMBER: _ClassVar[int] ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + DISCONNECT_REASON_FIELD_NUMBER: _ClassVar[int] + KIND_DETAILS_FIELD_NUMBER: _ClassVar[int] + DATA_TRACKS_FIELD_NUMBER: _ClassVar[int] + CLIENT_PROTOCOL_FIELD_NUMBER: _ClassVar[int] + CAPABILITIES_FIELD_NUMBER: _ClassVar[int] sid: str identity: str state: ParticipantInfo.State tracks: _containers.RepeatedCompositeFieldContainer[TrackInfo] metadata: str joined_at: int + joined_at_ms: int name: str version: int permission: ParticipantPermission @@ -274,7 +376,12 @@ class ParticipantInfo(_message.Message): is_publisher: bool kind: ParticipantInfo.Kind attributes: _containers.ScalarMap[str, str] - def __init__(self, sid: _Optional[str] = ..., identity: _Optional[str] = ..., state: _Optional[_Union[ParticipantInfo.State, str]] = ..., tracks: _Optional[_Iterable[_Union[TrackInfo, _Mapping]]] = ..., metadata: _Optional[str] = ..., joined_at: _Optional[int] = ..., name: _Optional[str] = ..., version: _Optional[int] = ..., permission: _Optional[_Union[ParticipantPermission, _Mapping]] = ..., region: _Optional[str] = ..., is_publisher: bool = ..., kind: _Optional[_Union[ParticipantInfo.Kind, str]] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + disconnect_reason: DisconnectReason + kind_details: _containers.RepeatedScalarFieldContainer[ParticipantInfo.KindDetail] + data_tracks: _containers.RepeatedCompositeFieldContainer[DataTrackInfo] + client_protocol: int + capabilities: _containers.RepeatedScalarFieldContainer[ClientInfo.Capability] + def __init__(self, sid: _Optional[str] = ..., identity: _Optional[str] = ..., state: _Optional[_Union[ParticipantInfo.State, str]] = ..., tracks: _Optional[_Iterable[_Union[TrackInfo, _Mapping]]] = ..., metadata: _Optional[str] = ..., joined_at: _Optional[int] = ..., joined_at_ms: _Optional[int] = ..., name: _Optional[str] = ..., version: _Optional[int] = ..., permission: _Optional[_Union[ParticipantPermission, _Mapping]] = ..., region: _Optional[str] = ..., is_publisher: bool = ..., kind: _Optional[_Union[ParticipantInfo.Kind, str]] = ..., attributes: _Optional[_Mapping[str, str]] = ..., disconnect_reason: _Optional[_Union[DisconnectReason, str]] = ..., kind_details: _Optional[_Iterable[_Union[ParticipantInfo.KindDetail, str]]] = ..., data_tracks: _Optional[_Iterable[_Union[DataTrackInfo, _Mapping]]] = ..., client_protocol: _Optional[int] = ..., capabilities: _Optional[_Iterable[_Union[ClientInfo.Capability, str]]] = ...) -> None: ... class Encryption(_message.Message): __slots__ = () @@ -289,19 +396,23 @@ class Encryption(_message.Message): def __init__(self) -> None: ... class SimulcastCodecInfo(_message.Message): - __slots__ = ("mime_type", "mid", "cid", "layers") + __slots__ = ("mime_type", "mid", "cid", "layers", "video_layer_mode", "sdp_cid") MIME_TYPE_FIELD_NUMBER: _ClassVar[int] MID_FIELD_NUMBER: _ClassVar[int] CID_FIELD_NUMBER: _ClassVar[int] LAYERS_FIELD_NUMBER: _ClassVar[int] + VIDEO_LAYER_MODE_FIELD_NUMBER: _ClassVar[int] + SDP_CID_FIELD_NUMBER: _ClassVar[int] mime_type: str mid: str cid: str layers: _containers.RepeatedCompositeFieldContainer[VideoLayer] - def __init__(self, mime_type: _Optional[str] = ..., mid: _Optional[str] = ..., cid: _Optional[str] = ..., layers: _Optional[_Iterable[_Union[VideoLayer, _Mapping]]] = ...) -> None: ... + video_layer_mode: VideoLayer.Mode + sdp_cid: str + def __init__(self, mime_type: _Optional[str] = ..., mid: _Optional[str] = ..., cid: _Optional[str] = ..., layers: _Optional[_Iterable[_Union[VideoLayer, _Mapping]]] = ..., video_layer_mode: _Optional[_Union[VideoLayer.Mode, str]] = ..., sdp_cid: _Optional[str] = ...) -> None: ... class TrackInfo(_message.Message): - __slots__ = ("sid", "type", "name", "muted", "width", "height", "simulcast", "disable_dtx", "source", "layers", "mime_type", "mid", "codecs", "stereo", "disable_red", "encryption", "stream", "version", "audio_features") + __slots__ = ("sid", "type", "name", "muted", "width", "height", "simulcast", "disable_dtx", "source", "layers", "mime_type", "mid", "codecs", "stereo", "disable_red", "encryption", "stream", "version", "audio_features", "backup_codec_policy", "packet_trailer_features") SID_FIELD_NUMBER: _ClassVar[int] TYPE_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] @@ -321,6 +432,8 @@ class TrackInfo(_message.Message): STREAM_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] AUDIO_FEATURES_FIELD_NUMBER: _ClassVar[int] + BACKUP_CODEC_POLICY_FIELD_NUMBER: _ClassVar[int] + PACKET_TRAILER_FEATURES_FIELD_NUMBER: _ClassVar[int] sid: str type: TrackType name: str @@ -340,24 +453,148 @@ class TrackInfo(_message.Message): stream: str version: TimedVersion audio_features: _containers.RepeatedScalarFieldContainer[AudioTrackFeature] - def __init__(self, sid: _Optional[str] = ..., type: _Optional[_Union[TrackType, str]] = ..., name: _Optional[str] = ..., muted: bool = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., simulcast: bool = ..., disable_dtx: bool = ..., source: _Optional[_Union[TrackSource, str]] = ..., layers: _Optional[_Iterable[_Union[VideoLayer, _Mapping]]] = ..., mime_type: _Optional[str] = ..., mid: _Optional[str] = ..., codecs: _Optional[_Iterable[_Union[SimulcastCodecInfo, _Mapping]]] = ..., stereo: bool = ..., disable_red: bool = ..., encryption: _Optional[_Union[Encryption.Type, str]] = ..., stream: _Optional[str] = ..., version: _Optional[_Union[TimedVersion, _Mapping]] = ..., audio_features: _Optional[_Iterable[_Union[AudioTrackFeature, str]]] = ...) -> None: ... + backup_codec_policy: BackupCodecPolicy + packet_trailer_features: _containers.RepeatedScalarFieldContainer[PacketTrailerFeature] + def __init__(self, sid: _Optional[str] = ..., type: _Optional[_Union[TrackType, str]] = ..., name: _Optional[str] = ..., muted: bool = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., simulcast: bool = ..., disable_dtx: bool = ..., source: _Optional[_Union[TrackSource, str]] = ..., layers: _Optional[_Iterable[_Union[VideoLayer, _Mapping]]] = ..., mime_type: _Optional[str] = ..., mid: _Optional[str] = ..., codecs: _Optional[_Iterable[_Union[SimulcastCodecInfo, _Mapping]]] = ..., stereo: bool = ..., disable_red: bool = ..., encryption: _Optional[_Union[Encryption.Type, str]] = ..., stream: _Optional[str] = ..., version: _Optional[_Union[TimedVersion, _Mapping]] = ..., audio_features: _Optional[_Iterable[_Union[AudioTrackFeature, str]]] = ..., backup_codec_policy: _Optional[_Union[BackupCodecPolicy, str]] = ..., packet_trailer_features: _Optional[_Iterable[_Union[PacketTrailerFeature, str]]] = ...) -> None: ... + +class DataTrackInfo(_message.Message): + __slots__ = ("pub_handle", "sid", "name", "encryption", "frame_encoding", "schema") + PUB_HANDLE_FIELD_NUMBER: _ClassVar[int] + SID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + FRAME_ENCODING_FIELD_NUMBER: _ClassVar[int] + SCHEMA_FIELD_NUMBER: _ClassVar[int] + pub_handle: int + sid: str + name: str + encryption: Encryption.Type + frame_encoding: DataTrackFrameEncoding + schema: DataTrackSchemaId + def __init__(self, pub_handle: _Optional[int] = ..., sid: _Optional[str] = ..., name: _Optional[str] = ..., encryption: _Optional[_Union[Encryption.Type, str]] = ..., frame_encoding: _Optional[_Union[DataTrackFrameEncoding, _Mapping]] = ..., schema: _Optional[_Union[DataTrackSchemaId, _Mapping]] = ...) -> None: ... + +class DataTrackFrameEncoding(_message.Message): + __slots__ = ("well_known", "custom") + class WellKnownFrameEncoding(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_ROS1: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_CDR: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_PROTOBUF: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_FLATBUFFER: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_CBOR: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_MSGPACK: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_JSON: _ClassVar[DataTrackFrameEncoding.WellKnownFrameEncoding] + WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_ROS1: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_CDR: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_PROTOBUF: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_FLATBUFFER: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_CBOR: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_MSGPACK: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FRAME_ENCODING_JSON: DataTrackFrameEncoding.WellKnownFrameEncoding + WELL_KNOWN_FIELD_NUMBER: _ClassVar[int] + CUSTOM_FIELD_NUMBER: _ClassVar[int] + well_known: DataTrackFrameEncoding.WellKnownFrameEncoding + custom: str + def __init__(self, well_known: _Optional[_Union[DataTrackFrameEncoding.WellKnownFrameEncoding, str]] = ..., custom: _Optional[str] = ...) -> None: ... + +class DataTrackSchemaEncoding(_message.Message): + __slots__ = ("well_known", "custom") + class WellKnownSchemaEncoding(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA: _ClassVar[DataTrackSchemaEncoding.WellKnownSchemaEncoding] + WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA: DataTrackSchemaEncoding.WellKnownSchemaEncoding + WELL_KNOWN_FIELD_NUMBER: _ClassVar[int] + CUSTOM_FIELD_NUMBER: _ClassVar[int] + well_known: DataTrackSchemaEncoding.WellKnownSchemaEncoding + custom: str + def __init__(self, well_known: _Optional[_Union[DataTrackSchemaEncoding.WellKnownSchemaEncoding, str]] = ..., custom: _Optional[str] = ...) -> None: ... + +class DataTrackSchemaId(_message.Message): + __slots__ = ("name", "encoding") + NAME_FIELD_NUMBER: _ClassVar[int] + ENCODING_FIELD_NUMBER: _ClassVar[int] + name: str + encoding: DataTrackSchemaEncoding + def __init__(self, name: _Optional[str] = ..., encoding: _Optional[_Union[DataTrackSchemaEncoding, _Mapping]] = ...) -> None: ... + +class DataTrackExtensionParticipantSid(_message.Message): + __slots__ = ("id", "participant_sid") + ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + id: DataTrackExtensionID + participant_sid: str + def __init__(self, id: _Optional[_Union[DataTrackExtensionID, str]] = ..., participant_sid: _Optional[str] = ...) -> None: ... + +class DataTrackSubscriptionOptions(_message.Message): + __slots__ = ("target_fps",) + TARGET_FPS_FIELD_NUMBER: _ClassVar[int] + target_fps: int + def __init__(self, target_fps: _Optional[int] = ...) -> None: ... + +class DataBlobKey(_message.Message): + __slots__ = ("generic", "schema_id") + GENERIC_FIELD_NUMBER: _ClassVar[int] + SCHEMA_ID_FIELD_NUMBER: _ClassVar[int] + generic: str + schema_id: DataTrackSchemaId + def __init__(self, generic: _Optional[str] = ..., schema_id: _Optional[_Union[DataTrackSchemaId, _Mapping]] = ...) -> None: ... + +class DataBlob(_message.Message): + __slots__ = ("key", "contents") + KEY_FIELD_NUMBER: _ClassVar[int] + CONTENTS_FIELD_NUMBER: _ClassVar[int] + key: DataBlobKey + contents: bytes + def __init__(self, key: _Optional[_Union[DataBlobKey, _Mapping]] = ..., contents: _Optional[bytes] = ...) -> None: ... class VideoLayer(_message.Message): - __slots__ = ("quality", "width", "height", "bitrate", "ssrc") + __slots__ = ("quality", "width", "height", "bitrate", "ssrc", "spatial_layer", "rid", "repair_ssrc") + class Mode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + MODE_UNUSED: _ClassVar[VideoLayer.Mode] + ONE_SPATIAL_LAYER_PER_STREAM: _ClassVar[VideoLayer.Mode] + MULTIPLE_SPATIAL_LAYERS_PER_STREAM: _ClassVar[VideoLayer.Mode] + ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR: _ClassVar[VideoLayer.Mode] + MODE_UNUSED: VideoLayer.Mode + ONE_SPATIAL_LAYER_PER_STREAM: VideoLayer.Mode + MULTIPLE_SPATIAL_LAYERS_PER_STREAM: VideoLayer.Mode + ONE_SPATIAL_LAYER_PER_STREAM_INCOMPLETE_RTCP_SR: VideoLayer.Mode QUALITY_FIELD_NUMBER: _ClassVar[int] WIDTH_FIELD_NUMBER: _ClassVar[int] HEIGHT_FIELD_NUMBER: _ClassVar[int] BITRATE_FIELD_NUMBER: _ClassVar[int] SSRC_FIELD_NUMBER: _ClassVar[int] + SPATIAL_LAYER_FIELD_NUMBER: _ClassVar[int] + RID_FIELD_NUMBER: _ClassVar[int] + REPAIR_SSRC_FIELD_NUMBER: _ClassVar[int] quality: VideoQuality width: int height: int bitrate: int ssrc: int - def __init__(self, quality: _Optional[_Union[VideoQuality, str]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., bitrate: _Optional[int] = ..., ssrc: _Optional[int] = ...) -> None: ... + spatial_layer: int + rid: str + repair_ssrc: int + def __init__(self, quality: _Optional[_Union[VideoQuality, str]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., bitrate: _Optional[int] = ..., ssrc: _Optional[int] = ..., spatial_layer: _Optional[int] = ..., rid: _Optional[str] = ..., repair_ssrc: _Optional[int] = ...) -> None: ... class DataPacket(_message.Message): - __slots__ = ("kind", "participant_identity", "destination_identities", "user", "speaker", "sip_dtmf", "transcription") + __slots__ = ("kind", "participant_identity", "destination_identities", "user", "speaker", "sip_dtmf", "transcription", "metrics", "chat_message", "rpc_request", "rpc_ack", "rpc_response", "stream_header", "stream_chunk", "stream_trailer", "encrypted_packet", "sequence", "participant_sid") class Kind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () RELIABLE: _ClassVar[DataPacket.Kind] @@ -371,6 +608,17 @@ class DataPacket(_message.Message): SPEAKER_FIELD_NUMBER: _ClassVar[int] SIP_DTMF_FIELD_NUMBER: _ClassVar[int] TRANSCRIPTION_FIELD_NUMBER: _ClassVar[int] + METRICS_FIELD_NUMBER: _ClassVar[int] + CHAT_MESSAGE_FIELD_NUMBER: _ClassVar[int] + RPC_REQUEST_FIELD_NUMBER: _ClassVar[int] + RPC_ACK_FIELD_NUMBER: _ClassVar[int] + RPC_RESPONSE_FIELD_NUMBER: _ClassVar[int] + STREAM_HEADER_FIELD_NUMBER: _ClassVar[int] + STREAM_CHUNK_FIELD_NUMBER: _ClassVar[int] + STREAM_TRAILER_FIELD_NUMBER: _ClassVar[int] + ENCRYPTED_PACKET_FIELD_NUMBER: _ClassVar[int] + SEQUENCE_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] kind: DataPacket.Kind participant_identity: str destination_identities: _containers.RepeatedScalarFieldContainer[str] @@ -378,7 +626,50 @@ class DataPacket(_message.Message): speaker: ActiveSpeakerUpdate sip_dtmf: SipDTMF transcription: Transcription - def __init__(self, kind: _Optional[_Union[DataPacket.Kind, str]] = ..., participant_identity: _Optional[str] = ..., destination_identities: _Optional[_Iterable[str]] = ..., user: _Optional[_Union[UserPacket, _Mapping]] = ..., speaker: _Optional[_Union[ActiveSpeakerUpdate, _Mapping]] = ..., sip_dtmf: _Optional[_Union[SipDTMF, _Mapping]] = ..., transcription: _Optional[_Union[Transcription, _Mapping]] = ...) -> None: ... + metrics: _metrics.MetricsBatch + chat_message: ChatMessage + rpc_request: RpcRequest + rpc_ack: RpcAck + rpc_response: RpcResponse + stream_header: DataStream.Header + stream_chunk: DataStream.Chunk + stream_trailer: DataStream.Trailer + encrypted_packet: EncryptedPacket + sequence: int + participant_sid: str + def __init__(self, kind: _Optional[_Union[DataPacket.Kind, str]] = ..., participant_identity: _Optional[str] = ..., destination_identities: _Optional[_Iterable[str]] = ..., user: _Optional[_Union[UserPacket, _Mapping]] = ..., speaker: _Optional[_Union[ActiveSpeakerUpdate, _Mapping]] = ..., sip_dtmf: _Optional[_Union[SipDTMF, _Mapping]] = ..., transcription: _Optional[_Union[Transcription, _Mapping]] = ..., metrics: _Optional[_Union[_metrics.MetricsBatch, _Mapping]] = ..., chat_message: _Optional[_Union[ChatMessage, _Mapping]] = ..., rpc_request: _Optional[_Union[RpcRequest, _Mapping]] = ..., rpc_ack: _Optional[_Union[RpcAck, _Mapping]] = ..., rpc_response: _Optional[_Union[RpcResponse, _Mapping]] = ..., stream_header: _Optional[_Union[DataStream.Header, _Mapping]] = ..., stream_chunk: _Optional[_Union[DataStream.Chunk, _Mapping]] = ..., stream_trailer: _Optional[_Union[DataStream.Trailer, _Mapping]] = ..., encrypted_packet: _Optional[_Union[EncryptedPacket, _Mapping]] = ..., sequence: _Optional[int] = ..., participant_sid: _Optional[str] = ...) -> None: ... + +class EncryptedPacket(_message.Message): + __slots__ = ("encryption_type", "iv", "key_index", "encrypted_value") + ENCRYPTION_TYPE_FIELD_NUMBER: _ClassVar[int] + IV_FIELD_NUMBER: _ClassVar[int] + KEY_INDEX_FIELD_NUMBER: _ClassVar[int] + ENCRYPTED_VALUE_FIELD_NUMBER: _ClassVar[int] + encryption_type: Encryption.Type + iv: bytes + key_index: int + encrypted_value: bytes + def __init__(self, encryption_type: _Optional[_Union[Encryption.Type, str]] = ..., iv: _Optional[bytes] = ..., key_index: _Optional[int] = ..., encrypted_value: _Optional[bytes] = ...) -> None: ... + +class EncryptedPacketPayload(_message.Message): + __slots__ = ("user", "chat_message", "rpc_request", "rpc_ack", "rpc_response", "stream_header", "stream_chunk", "stream_trailer") + USER_FIELD_NUMBER: _ClassVar[int] + CHAT_MESSAGE_FIELD_NUMBER: _ClassVar[int] + RPC_REQUEST_FIELD_NUMBER: _ClassVar[int] + RPC_ACK_FIELD_NUMBER: _ClassVar[int] + RPC_RESPONSE_FIELD_NUMBER: _ClassVar[int] + STREAM_HEADER_FIELD_NUMBER: _ClassVar[int] + STREAM_CHUNK_FIELD_NUMBER: _ClassVar[int] + STREAM_TRAILER_FIELD_NUMBER: _ClassVar[int] + user: UserPacket + chat_message: ChatMessage + rpc_request: RpcRequest + rpc_ack: RpcAck + rpc_response: RpcResponse + stream_header: DataStream.Header + stream_chunk: DataStream.Chunk + stream_trailer: DataStream.Trailer + def __init__(self, user: _Optional[_Union[UserPacket, _Mapping]] = ..., chat_message: _Optional[_Union[ChatMessage, _Mapping]] = ..., rpc_request: _Optional[_Union[RpcRequest, _Mapping]] = ..., rpc_ack: _Optional[_Union[RpcAck, _Mapping]] = ..., rpc_response: _Optional[_Union[RpcResponse, _Mapping]] = ..., stream_header: _Optional[_Union[DataStream.Header, _Mapping]] = ..., stream_chunk: _Optional[_Union[DataStream.Chunk, _Mapping]] = ..., stream_trailer: _Optional[_Union[DataStream.Trailer, _Mapping]] = ...) -> None: ... class ActiveSpeakerUpdate(_message.Message): __slots__ = ("speakers",) @@ -397,7 +688,7 @@ class SpeakerInfo(_message.Message): def __init__(self, sid: _Optional[str] = ..., level: _Optional[float] = ..., active: bool = ...) -> None: ... class UserPacket(_message.Message): - __slots__ = ("participant_sid", "participant_identity", "payload", "destination_sids", "destination_identities", "topic", "id", "start_time", "end_time") + __slots__ = ("participant_sid", "participant_identity", "payload", "destination_sids", "destination_identities", "topic", "id", "start_time", "end_time", "nonce") PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] PAYLOAD_FIELD_NUMBER: _ClassVar[int] @@ -407,6 +698,7 @@ class UserPacket(_message.Message): ID_FIELD_NUMBER: _ClassVar[int] START_TIME_FIELD_NUMBER: _ClassVar[int] END_TIME_FIELD_NUMBER: _ClassVar[int] + NONCE_FIELD_NUMBER: _ClassVar[int] participant_sid: str participant_identity: str payload: bytes @@ -416,7 +708,8 @@ class UserPacket(_message.Message): id: str start_time: int end_time: int - def __init__(self, participant_sid: _Optional[str] = ..., participant_identity: _Optional[str] = ..., payload: _Optional[bytes] = ..., destination_sids: _Optional[_Iterable[str]] = ..., destination_identities: _Optional[_Iterable[str]] = ..., topic: _Optional[str] = ..., id: _Optional[str] = ..., start_time: _Optional[int] = ..., end_time: _Optional[int] = ...) -> None: ... + nonce: bytes + def __init__(self, participant_sid: _Optional[str] = ..., participant_identity: _Optional[str] = ..., payload: _Optional[bytes] = ..., destination_sids: _Optional[_Iterable[str]] = ..., destination_identities: _Optional[_Iterable[str]] = ..., topic: _Optional[str] = ..., id: _Optional[str] = ..., start_time: _Optional[int] = ..., end_time: _Optional[int] = ..., nonce: _Optional[bytes] = ...) -> None: ... class SipDTMF(_message.Message): __slots__ = ("code", "digit") @@ -452,6 +745,66 @@ class TranscriptionSegment(_message.Message): language: str def __init__(self, id: _Optional[str] = ..., text: _Optional[str] = ..., start_time: _Optional[int] = ..., end_time: _Optional[int] = ..., final: bool = ..., language: _Optional[str] = ...) -> None: ... +class ChatMessage(_message.Message): + __slots__ = ("id", "timestamp", "edit_timestamp", "message", "deleted", "generated") + ID_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + EDIT_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + DELETED_FIELD_NUMBER: _ClassVar[int] + GENERATED_FIELD_NUMBER: _ClassVar[int] + id: str + timestamp: int + edit_timestamp: int + message: str + deleted: bool + generated: bool + def __init__(self, id: _Optional[str] = ..., timestamp: _Optional[int] = ..., edit_timestamp: _Optional[int] = ..., message: _Optional[str] = ..., deleted: bool = ..., generated: bool = ...) -> None: ... + +class RpcRequest(_message.Message): + __slots__ = ("id", "method", "payload", "response_timeout_ms", "version", "compressed_payload") + ID_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + RESPONSE_TIMEOUT_MS_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + COMPRESSED_PAYLOAD_FIELD_NUMBER: _ClassVar[int] + id: str + method: str + payload: str + response_timeout_ms: int + version: int + compressed_payload: bytes + def __init__(self, id: _Optional[str] = ..., method: _Optional[str] = ..., payload: _Optional[str] = ..., response_timeout_ms: _Optional[int] = ..., version: _Optional[int] = ..., compressed_payload: _Optional[bytes] = ...) -> None: ... + +class RpcAck(_message.Message): + __slots__ = ("request_id",) + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + request_id: str + def __init__(self, request_id: _Optional[str] = ...) -> None: ... + +class RpcResponse(_message.Message): + __slots__ = ("request_id", "payload", "error", "compressed_payload") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + COMPRESSED_PAYLOAD_FIELD_NUMBER: _ClassVar[int] + request_id: str + payload: str + error: RpcError + compressed_payload: bytes + def __init__(self, request_id: _Optional[str] = ..., payload: _Optional[str] = ..., error: _Optional[_Union[RpcError, _Mapping]] = ..., compressed_payload: _Optional[bytes] = ...) -> None: ... + +class RpcError(_message.Message): + __slots__ = ("code", "message", "data") + CODE_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + DATA_FIELD_NUMBER: _ClassVar[int] + code: int + message: str + data: str + def __init__(self, code: _Optional[int] = ..., message: _Optional[str] = ..., data: _Optional[str] = ...) -> None: ... + class ParticipantTracks(_message.Message): __slots__ = ("participant_sid", "track_sids") PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] @@ -485,7 +838,7 @@ class ServerInfo(_message.Message): def __init__(self, edition: _Optional[_Union[ServerInfo.Edition, str]] = ..., version: _Optional[str] = ..., protocol: _Optional[int] = ..., region: _Optional[str] = ..., node_id: _Optional[str] = ..., debug_info: _Optional[str] = ..., agent_protocol: _Optional[int] = ...) -> None: ... class ClientInfo(_message.Message): - __slots__ = ("sdk", "version", "protocol", "os", "os_version", "device_model", "browser", "browser_version", "address", "network") + __slots__ = ("sdk", "version", "protocol", "os", "os_version", "device_model", "browser", "browser_version", "address", "network", "other_sdks", "client_protocol", "capabilities") class SDK(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () UNKNOWN: _ClassVar[ClientInfo.SDK] @@ -499,6 +852,10 @@ class ClientInfo(_message.Message): RUST: _ClassVar[ClientInfo.SDK] PYTHON: _ClassVar[ClientInfo.SDK] CPP: _ClassVar[ClientInfo.SDK] + UNITY_WEB: _ClassVar[ClientInfo.SDK] + NODE: _ClassVar[ClientInfo.SDK] + UNREAL: _ClassVar[ClientInfo.SDK] + ESP32: _ClassVar[ClientInfo.SDK] UNKNOWN: ClientInfo.SDK JS: ClientInfo.SDK SWIFT: ClientInfo.SDK @@ -510,6 +867,18 @@ class ClientInfo(_message.Message): RUST: ClientInfo.SDK PYTHON: ClientInfo.SDK CPP: ClientInfo.SDK + UNITY_WEB: ClientInfo.SDK + NODE: ClientInfo.SDK + UNREAL: ClientInfo.SDK + ESP32: ClientInfo.SDK + class Capability(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CAP_UNUSED: _ClassVar[ClientInfo.Capability] + CAP_PACKET_TRAILER: _ClassVar[ClientInfo.Capability] + CAP_COMPRESSION_DEFLATE_RAW: _ClassVar[ClientInfo.Capability] + CAP_UNUSED: ClientInfo.Capability + CAP_PACKET_TRAILER: ClientInfo.Capability + CAP_COMPRESSION_DEFLATE_RAW: ClientInfo.Capability SDK_FIELD_NUMBER: _ClassVar[int] VERSION_FIELD_NUMBER: _ClassVar[int] PROTOCOL_FIELD_NUMBER: _ClassVar[int] @@ -520,6 +889,9 @@ class ClientInfo(_message.Message): BROWSER_VERSION_FIELD_NUMBER: _ClassVar[int] ADDRESS_FIELD_NUMBER: _ClassVar[int] NETWORK_FIELD_NUMBER: _ClassVar[int] + OTHER_SDKS_FIELD_NUMBER: _ClassVar[int] + CLIENT_PROTOCOL_FIELD_NUMBER: _ClassVar[int] + CAPABILITIES_FIELD_NUMBER: _ClassVar[int] sdk: ClientInfo.SDK version: str protocol: int @@ -530,7 +902,10 @@ class ClientInfo(_message.Message): browser_version: str address: str network: str - def __init__(self, sdk: _Optional[_Union[ClientInfo.SDK, str]] = ..., version: _Optional[str] = ..., protocol: _Optional[int] = ..., os: _Optional[str] = ..., os_version: _Optional[str] = ..., device_model: _Optional[str] = ..., browser: _Optional[str] = ..., browser_version: _Optional[str] = ..., address: _Optional[str] = ..., network: _Optional[str] = ...) -> None: ... + other_sdks: str + client_protocol: int + capabilities: _containers.RepeatedScalarFieldContainer[ClientInfo.Capability] + def __init__(self, sdk: _Optional[_Union[ClientInfo.SDK, str]] = ..., version: _Optional[str] = ..., protocol: _Optional[int] = ..., os: _Optional[str] = ..., os_version: _Optional[str] = ..., device_model: _Optional[str] = ..., browser: _Optional[str] = ..., browser_version: _Optional[str] = ..., address: _Optional[str] = ..., network: _Optional[str] = ..., other_sdks: _Optional[str] = ..., client_protocol: _Optional[int] = ..., capabilities: _Optional[_Iterable[_Union[ClientInfo.Capability, str]]] = ...) -> None: ... class ClientConfiguration(_message.Message): __slots__ = ("video", "screen", "resume_connection", "disabled_codecs", "force_relay") @@ -583,7 +958,7 @@ class RTPDrift(_message.Message): def __init__(self, start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., end_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., duration: _Optional[float] = ..., start_timestamp: _Optional[int] = ..., end_timestamp: _Optional[int] = ..., rtp_clock_ticks: _Optional[int] = ..., drift_samples: _Optional[int] = ..., drift_ms: _Optional[float] = ..., clock_rate: _Optional[float] = ...) -> None: ... class RTPStats(_message.Message): - __slots__ = ("start_time", "end_time", "duration", "packets", "packet_rate", "bytes", "header_bytes", "bitrate", "packets_lost", "packet_loss_rate", "packet_loss_percentage", "packets_duplicate", "packet_duplicate_rate", "bytes_duplicate", "header_bytes_duplicate", "bitrate_duplicate", "packets_padding", "packet_padding_rate", "bytes_padding", "header_bytes_padding", "bitrate_padding", "packets_out_of_order", "frames", "frame_rate", "jitter_current", "jitter_max", "gap_histogram", "nacks", "nack_acks", "nack_misses", "nack_repeated", "plis", "last_pli", "firs", "last_fir", "rtt_current", "rtt_max", "key_frames", "last_key_frame", "layer_lock_plis", "last_layer_lock_pli", "packet_drift", "report_drift", "rebased_report_drift") + __slots__ = ("start_time", "end_time", "duration", "packets", "packet_rate", "bytes", "header_bytes", "bitrate", "packets_lost", "packet_loss_rate", "packet_loss_percentage", "packets_duplicate", "packet_duplicate_rate", "bytes_duplicate", "header_bytes_duplicate", "bitrate_duplicate", "packets_padding", "packet_padding_rate", "bytes_padding", "header_bytes_padding", "bitrate_padding", "packets_out_of_order", "frames", "frame_rate", "jitter_current", "jitter_max", "gap_histogram", "nacks", "nack_acks", "nack_misses", "nack_repeated", "plis", "last_pli", "firs", "last_fir", "rtt_current", "rtt_max", "key_frames", "last_key_frame", "layer_lock_plis", "last_layer_lock_pli", "packet_drift", "ntp_report_drift", "rebased_report_drift", "received_report_drift") class GapHistogramEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -633,8 +1008,9 @@ class RTPStats(_message.Message): LAYER_LOCK_PLIS_FIELD_NUMBER: _ClassVar[int] LAST_LAYER_LOCK_PLI_FIELD_NUMBER: _ClassVar[int] PACKET_DRIFT_FIELD_NUMBER: _ClassVar[int] - REPORT_DRIFT_FIELD_NUMBER: _ClassVar[int] + NTP_REPORT_DRIFT_FIELD_NUMBER: _ClassVar[int] REBASED_REPORT_DRIFT_FIELD_NUMBER: _ClassVar[int] + RECEIVED_REPORT_DRIFT_FIELD_NUMBER: _ClassVar[int] start_time: _timestamp_pb2.Timestamp end_time: _timestamp_pb2.Timestamp duration: float @@ -677,9 +1053,82 @@ class RTPStats(_message.Message): layer_lock_plis: int last_layer_lock_pli: _timestamp_pb2.Timestamp packet_drift: RTPDrift - report_drift: RTPDrift + ntp_report_drift: RTPDrift rebased_report_drift: RTPDrift - def __init__(self, start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., end_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., duration: _Optional[float] = ..., packets: _Optional[int] = ..., packet_rate: _Optional[float] = ..., bytes: _Optional[int] = ..., header_bytes: _Optional[int] = ..., bitrate: _Optional[float] = ..., packets_lost: _Optional[int] = ..., packet_loss_rate: _Optional[float] = ..., packet_loss_percentage: _Optional[float] = ..., packets_duplicate: _Optional[int] = ..., packet_duplicate_rate: _Optional[float] = ..., bytes_duplicate: _Optional[int] = ..., header_bytes_duplicate: _Optional[int] = ..., bitrate_duplicate: _Optional[float] = ..., packets_padding: _Optional[int] = ..., packet_padding_rate: _Optional[float] = ..., bytes_padding: _Optional[int] = ..., header_bytes_padding: _Optional[int] = ..., bitrate_padding: _Optional[float] = ..., packets_out_of_order: _Optional[int] = ..., frames: _Optional[int] = ..., frame_rate: _Optional[float] = ..., jitter_current: _Optional[float] = ..., jitter_max: _Optional[float] = ..., gap_histogram: _Optional[_Mapping[int, int]] = ..., nacks: _Optional[int] = ..., nack_acks: _Optional[int] = ..., nack_misses: _Optional[int] = ..., nack_repeated: _Optional[int] = ..., plis: _Optional[int] = ..., last_pli: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., firs: _Optional[int] = ..., last_fir: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., rtt_current: _Optional[int] = ..., rtt_max: _Optional[int] = ..., key_frames: _Optional[int] = ..., last_key_frame: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., layer_lock_plis: _Optional[int] = ..., last_layer_lock_pli: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., packet_drift: _Optional[_Union[RTPDrift, _Mapping]] = ..., report_drift: _Optional[_Union[RTPDrift, _Mapping]] = ..., rebased_report_drift: _Optional[_Union[RTPDrift, _Mapping]] = ...) -> None: ... + received_report_drift: RTPDrift + def __init__(self, start_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., end_time: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., duration: _Optional[float] = ..., packets: _Optional[int] = ..., packet_rate: _Optional[float] = ..., bytes: _Optional[int] = ..., header_bytes: _Optional[int] = ..., bitrate: _Optional[float] = ..., packets_lost: _Optional[int] = ..., packet_loss_rate: _Optional[float] = ..., packet_loss_percentage: _Optional[float] = ..., packets_duplicate: _Optional[int] = ..., packet_duplicate_rate: _Optional[float] = ..., bytes_duplicate: _Optional[int] = ..., header_bytes_duplicate: _Optional[int] = ..., bitrate_duplicate: _Optional[float] = ..., packets_padding: _Optional[int] = ..., packet_padding_rate: _Optional[float] = ..., bytes_padding: _Optional[int] = ..., header_bytes_padding: _Optional[int] = ..., bitrate_padding: _Optional[float] = ..., packets_out_of_order: _Optional[int] = ..., frames: _Optional[int] = ..., frame_rate: _Optional[float] = ..., jitter_current: _Optional[float] = ..., jitter_max: _Optional[float] = ..., gap_histogram: _Optional[_Mapping[int, int]] = ..., nacks: _Optional[int] = ..., nack_acks: _Optional[int] = ..., nack_misses: _Optional[int] = ..., nack_repeated: _Optional[int] = ..., plis: _Optional[int] = ..., last_pli: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., firs: _Optional[int] = ..., last_fir: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., rtt_current: _Optional[int] = ..., rtt_max: _Optional[int] = ..., key_frames: _Optional[int] = ..., last_key_frame: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., layer_lock_plis: _Optional[int] = ..., last_layer_lock_pli: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., packet_drift: _Optional[_Union[RTPDrift, _Mapping]] = ..., ntp_report_drift: _Optional[_Union[RTPDrift, _Mapping]] = ..., rebased_report_drift: _Optional[_Union[RTPDrift, _Mapping]] = ..., received_report_drift: _Optional[_Union[RTPDrift, _Mapping]] = ...) -> None: ... + +class RTCPSenderReportState(_message.Message): + __slots__ = ("rtp_timestamp", "rtp_timestamp_ext", "ntp_timestamp", "at", "at_adjusted", "packets", "octets") + RTP_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + RTP_TIMESTAMP_EXT_FIELD_NUMBER: _ClassVar[int] + NTP_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + AT_FIELD_NUMBER: _ClassVar[int] + AT_ADJUSTED_FIELD_NUMBER: _ClassVar[int] + PACKETS_FIELD_NUMBER: _ClassVar[int] + OCTETS_FIELD_NUMBER: _ClassVar[int] + rtp_timestamp: int + rtp_timestamp_ext: int + ntp_timestamp: int + at: int + at_adjusted: int + packets: int + octets: int + def __init__(self, rtp_timestamp: _Optional[int] = ..., rtp_timestamp_ext: _Optional[int] = ..., ntp_timestamp: _Optional[int] = ..., at: _Optional[int] = ..., at_adjusted: _Optional[int] = ..., packets: _Optional[int] = ..., octets: _Optional[int] = ...) -> None: ... + +class RTPForwarderState(_message.Message): + __slots__ = ("started", "reference_layer_spatial", "pre_start_time", "ext_first_timestamp", "dummy_start_timestamp_offset", "rtp_munger", "vp8_munger", "sender_report_state") + STARTED_FIELD_NUMBER: _ClassVar[int] + REFERENCE_LAYER_SPATIAL_FIELD_NUMBER: _ClassVar[int] + PRE_START_TIME_FIELD_NUMBER: _ClassVar[int] + EXT_FIRST_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + DUMMY_START_TIMESTAMP_OFFSET_FIELD_NUMBER: _ClassVar[int] + RTP_MUNGER_FIELD_NUMBER: _ClassVar[int] + VP8_MUNGER_FIELD_NUMBER: _ClassVar[int] + SENDER_REPORT_STATE_FIELD_NUMBER: _ClassVar[int] + started: bool + reference_layer_spatial: int + pre_start_time: int + ext_first_timestamp: int + dummy_start_timestamp_offset: int + rtp_munger: RTPMungerState + vp8_munger: VP8MungerState + sender_report_state: _containers.RepeatedCompositeFieldContainer[RTCPSenderReportState] + def __init__(self, started: bool = ..., reference_layer_spatial: _Optional[int] = ..., pre_start_time: _Optional[int] = ..., ext_first_timestamp: _Optional[int] = ..., dummy_start_timestamp_offset: _Optional[int] = ..., rtp_munger: _Optional[_Union[RTPMungerState, _Mapping]] = ..., vp8_munger: _Optional[_Union[VP8MungerState, _Mapping]] = ..., sender_report_state: _Optional[_Iterable[_Union[RTCPSenderReportState, _Mapping]]] = ...) -> None: ... + +class RTPMungerState(_message.Message): + __slots__ = ("ext_last_sequence_number", "ext_second_last_sequence_number", "ext_last_timestamp", "ext_second_last_timestamp", "last_marker", "second_last_marker") + EXT_LAST_SEQUENCE_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXT_SECOND_LAST_SEQUENCE_NUMBER_FIELD_NUMBER: _ClassVar[int] + EXT_LAST_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + EXT_SECOND_LAST_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + LAST_MARKER_FIELD_NUMBER: _ClassVar[int] + SECOND_LAST_MARKER_FIELD_NUMBER: _ClassVar[int] + ext_last_sequence_number: int + ext_second_last_sequence_number: int + ext_last_timestamp: int + ext_second_last_timestamp: int + last_marker: bool + second_last_marker: bool + def __init__(self, ext_last_sequence_number: _Optional[int] = ..., ext_second_last_sequence_number: _Optional[int] = ..., ext_last_timestamp: _Optional[int] = ..., ext_second_last_timestamp: _Optional[int] = ..., last_marker: bool = ..., second_last_marker: bool = ...) -> None: ... + +class VP8MungerState(_message.Message): + __slots__ = ("ext_last_picture_id", "picture_id_used", "last_tl0_pic_idx", "tl0_pic_idx_used", "tid_used", "last_key_idx", "key_idx_used") + EXT_LAST_PICTURE_ID_FIELD_NUMBER: _ClassVar[int] + PICTURE_ID_USED_FIELD_NUMBER: _ClassVar[int] + LAST_TL0_PIC_IDX_FIELD_NUMBER: _ClassVar[int] + TL0_PIC_IDX_USED_FIELD_NUMBER: _ClassVar[int] + TID_USED_FIELD_NUMBER: _ClassVar[int] + LAST_KEY_IDX_FIELD_NUMBER: _ClassVar[int] + KEY_IDX_USED_FIELD_NUMBER: _ClassVar[int] + ext_last_picture_id: int + picture_id_used: bool + last_tl0_pic_idx: int + tl0_pic_idx_used: bool + tid_used: bool + last_key_idx: int + key_idx_used: bool + def __init__(self, ext_last_picture_id: _Optional[int] = ..., picture_id_used: bool = ..., last_tl0_pic_idx: _Optional[int] = ..., tl0_pic_idx_used: bool = ..., tid_used: bool = ..., last_key_idx: _Optional[int] = ..., key_idx_used: bool = ...) -> None: ... class TimedVersion(_message.Message): __slots__ = ("unix_micro", "ticks") @@ -688,3 +1137,128 @@ class TimedVersion(_message.Message): unix_micro: int ticks: int def __init__(self, unix_micro: _Optional[int] = ..., ticks: _Optional[int] = ...) -> None: ... + +class DataStream(_message.Message): + __slots__ = () + class OperationType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + CREATE: _ClassVar[DataStream.OperationType] + UPDATE: _ClassVar[DataStream.OperationType] + DELETE: _ClassVar[DataStream.OperationType] + REACTION: _ClassVar[DataStream.OperationType] + CREATE: DataStream.OperationType + UPDATE: DataStream.OperationType + DELETE: DataStream.OperationType + REACTION: DataStream.OperationType + class CompressionType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NONE: _ClassVar[DataStream.CompressionType] + DEFLATE_RAW: _ClassVar[DataStream.CompressionType] + NONE: DataStream.CompressionType + DEFLATE_RAW: DataStream.CompressionType + class TextHeader(_message.Message): + __slots__ = ("operation_type", "version", "reply_to_stream_id", "attached_stream_ids", "generated") + OPERATION_TYPE_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + REPLY_TO_STREAM_ID_FIELD_NUMBER: _ClassVar[int] + ATTACHED_STREAM_IDS_FIELD_NUMBER: _ClassVar[int] + GENERATED_FIELD_NUMBER: _ClassVar[int] + operation_type: DataStream.OperationType + version: int + reply_to_stream_id: str + attached_stream_ids: _containers.RepeatedScalarFieldContainer[str] + generated: bool + def __init__(self, operation_type: _Optional[_Union[DataStream.OperationType, str]] = ..., version: _Optional[int] = ..., reply_to_stream_id: _Optional[str] = ..., attached_stream_ids: _Optional[_Iterable[str]] = ..., generated: bool = ...) -> None: ... + class ByteHeader(_message.Message): + __slots__ = ("name",) + NAME_FIELD_NUMBER: _ClassVar[int] + name: str + def __init__(self, name: _Optional[str] = ...) -> None: ... + class Header(_message.Message): + __slots__ = ("stream_id", "timestamp", "topic", "mime_type", "total_length", "encryption_type", "attributes", "text_header", "byte_header", "inline_content", "compression") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + STREAM_ID_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + TOPIC_FIELD_NUMBER: _ClassVar[int] + MIME_TYPE_FIELD_NUMBER: _ClassVar[int] + TOTAL_LENGTH_FIELD_NUMBER: _ClassVar[int] + ENCRYPTION_TYPE_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + TEXT_HEADER_FIELD_NUMBER: _ClassVar[int] + BYTE_HEADER_FIELD_NUMBER: _ClassVar[int] + INLINE_CONTENT_FIELD_NUMBER: _ClassVar[int] + COMPRESSION_FIELD_NUMBER: _ClassVar[int] + stream_id: str + timestamp: int + topic: str + mime_type: str + total_length: int + encryption_type: Encryption.Type + attributes: _containers.ScalarMap[str, str] + text_header: DataStream.TextHeader + byte_header: DataStream.ByteHeader + inline_content: bytes + compression: DataStream.CompressionType + def __init__(self, stream_id: _Optional[str] = ..., timestamp: _Optional[int] = ..., topic: _Optional[str] = ..., mime_type: _Optional[str] = ..., total_length: _Optional[int] = ..., encryption_type: _Optional[_Union[Encryption.Type, str]] = ..., attributes: _Optional[_Mapping[str, str]] = ..., text_header: _Optional[_Union[DataStream.TextHeader, _Mapping]] = ..., byte_header: _Optional[_Union[DataStream.ByteHeader, _Mapping]] = ..., inline_content: _Optional[bytes] = ..., compression: _Optional[_Union[DataStream.CompressionType, str]] = ...) -> None: ... + class Chunk(_message.Message): + __slots__ = ("stream_id", "chunk_index", "content", "version", "iv") + STREAM_ID_FIELD_NUMBER: _ClassVar[int] + CHUNK_INDEX_FIELD_NUMBER: _ClassVar[int] + CONTENT_FIELD_NUMBER: _ClassVar[int] + VERSION_FIELD_NUMBER: _ClassVar[int] + IV_FIELD_NUMBER: _ClassVar[int] + stream_id: str + chunk_index: int + content: bytes + version: int + iv: bytes + def __init__(self, stream_id: _Optional[str] = ..., chunk_index: _Optional[int] = ..., content: _Optional[bytes] = ..., version: _Optional[int] = ..., iv: _Optional[bytes] = ...) -> None: ... + class Trailer(_message.Message): + __slots__ = ("stream_id", "reason", "attributes") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + STREAM_ID_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + stream_id: str + reason: str + attributes: _containers.ScalarMap[str, str] + def __init__(self, stream_id: _Optional[str] = ..., reason: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + def __init__(self) -> None: ... + +class FilterParams(_message.Message): + __slots__ = ("include_events", "exclude_events") + INCLUDE_EVENTS_FIELD_NUMBER: _ClassVar[int] + EXCLUDE_EVENTS_FIELD_NUMBER: _ClassVar[int] + include_events: _containers.RepeatedScalarFieldContainer[str] + exclude_events: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, include_events: _Optional[_Iterable[str]] = ..., exclude_events: _Optional[_Iterable[str]] = ...) -> None: ... + +class WebhookConfig(_message.Message): + __slots__ = ("url", "signing_key", "filter_params") + URL_FIELD_NUMBER: _ClassVar[int] + SIGNING_KEY_FIELD_NUMBER: _ClassVar[int] + FILTER_PARAMS_FIELD_NUMBER: _ClassVar[int] + url: str + signing_key: str + filter_params: FilterParams + def __init__(self, url: _Optional[str] = ..., signing_key: _Optional[str] = ..., filter_params: _Optional[_Union[FilterParams, _Mapping]] = ...) -> None: ... + +class SubscribedAudioCodec(_message.Message): + __slots__ = ("codec", "enabled") + CODEC_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] + codec: str + enabled: bool + def __init__(self, codec: _Optional[str] = ..., enabled: bool = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/room.py b/livekit-protocol/livekit/protocol/room.py index 2caebeb8..694b107c 100644 --- a/livekit-protocol/livekit/protocol/room.py +++ b/livekit-protocol/livekit/protocol/room.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_room.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_room.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,58 +24,98 @@ from . import models as _models_ from . import egress as _egress_ +from . import agent_dispatch as _agent__dispatch_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12livekit_room.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14livekit_egress.proto\"\x81\x02\n\x11\x43reateRoomRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rempty_timeout\x18\x02 \x01(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\n \x01(\r\x12\x18\n\x10max_participants\x18\x03 \x01(\r\x12\x0f\n\x07node_id\x18\x04 \x01(\t\x12\x10\n\x08metadata\x18\x05 \x01(\t\x12#\n\x06\x65gress\x18\x06 \x01(\x0b\x32\x13.livekit.RoomEgress\x12\x19\n\x11min_playout_delay\x18\x07 \x01(\r\x12\x19\n\x11max_playout_delay\x18\x08 \x01(\r\x12\x14\n\x0csync_streams\x18\t \x01(\x08\"\x9e\x01\n\nRoomEgress\x12\x31\n\x04room\x18\x01 \x01(\x0b\x32#.livekit.RoomCompositeEgressRequest\x12\x33\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x1e.livekit.AutoParticipantEgress\x12(\n\x06tracks\x18\x02 \x01(\x0b\x32\x18.livekit.AutoTrackEgress\"!\n\x10ListRoomsRequest\x12\r\n\x05names\x18\x01 \x03(\t\"1\n\x11ListRoomsResponse\x12\x1c\n\x05rooms\x18\x01 \x03(\x0b\x32\r.livekit.Room\"!\n\x11\x44\x65leteRoomRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\"\x14\n\x12\x44\x65leteRoomResponse\"\'\n\x17ListParticipantsRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\"J\n\x18ListParticipantsResponse\x12.\n\x0cparticipants\x18\x01 \x03(\x0b\x32\x18.livekit.ParticipantInfo\"9\n\x17RoomParticipantIdentity\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\"\x1b\n\x19RemoveParticipantResponse\"X\n\x14MuteRoomTrackRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x11\n\ttrack_sid\x18\x03 \x01(\t\x12\r\n\x05muted\x18\x04 \x01(\x08\":\n\x15MuteRoomTrackResponse\x12!\n\x05track\x18\x01 \x01(\x0b\x32\x12.livekit.TrackInfo\"\x88\x02\n\x18UpdateParticipantRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x10\n\x08metadata\x18\x03 \x01(\t\x12\x32\n\npermission\x18\x04 \x01(\x0b\x32\x1e.livekit.ParticipantPermission\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x45\n\nattributes\x18\x06 \x03(\x0b\x32\x31.livekit.UpdateParticipantRequest.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9b\x01\n\x1aUpdateSubscriptionsRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntrack_sids\x18\x03 \x03(\t\x12\x11\n\tsubscribe\x18\x04 \x01(\x08\x12\x36\n\x12participant_tracks\x18\x05 \x03(\x0b\x32\x1a.livekit.ParticipantTracks\"\x1d\n\x1bUpdateSubscriptionsResponse\"\xb1\x01\n\x0fSendDataRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12&\n\x04kind\x18\x03 \x01(\x0e\x32\x18.livekit.DataPacket.Kind\x12\x1c\n\x10\x64\x65stination_sids\x18\x04 \x03(\tB\x02\x18\x01\x12\x1e\n\x16\x64\x65stination_identities\x18\x06 \x03(\t\x12\x12\n\x05topic\x18\x05 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_topic\"\x12\n\x10SendDataResponse\";\n\x19UpdateRoomMetadataRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08metadata\x18\x02 \x01(\t2\xe6\x06\n\x0bRoomService\x12\x37\n\nCreateRoom\x12\x1a.livekit.CreateRoomRequest\x1a\r.livekit.Room\x12\x42\n\tListRooms\x12\x19.livekit.ListRoomsRequest\x1a\x1a.livekit.ListRoomsResponse\x12\x45\n\nDeleteRoom\x12\x1a.livekit.DeleteRoomRequest\x1a\x1b.livekit.DeleteRoomResponse\x12W\n\x10ListParticipants\x12 .livekit.ListParticipantsRequest\x1a!.livekit.ListParticipantsResponse\x12L\n\x0eGetParticipant\x12 .livekit.RoomParticipantIdentity\x1a\x18.livekit.ParticipantInfo\x12Y\n\x11RemoveParticipant\x12 .livekit.RoomParticipantIdentity\x1a\".livekit.RemoveParticipantResponse\x12S\n\x12MutePublishedTrack\x12\x1d.livekit.MuteRoomTrackRequest\x1a\x1e.livekit.MuteRoomTrackResponse\x12P\n\x11UpdateParticipant\x12!.livekit.UpdateParticipantRequest\x1a\x18.livekit.ParticipantInfo\x12`\n\x13UpdateSubscriptions\x12#.livekit.UpdateSubscriptionsRequest\x1a$.livekit.UpdateSubscriptionsResponse\x12?\n\x08SendData\x12\x18.livekit.SendDataRequest\x1a\x19.livekit.SendDataResponse\x12G\n\x12UpdateRoomMetadata\x12\".livekit.UpdateRoomMetadataRequest\x1a\r.livekit.RoomBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x12livekit_room.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14livekit_egress.proto\x1a\x1clivekit_agent_dispatch.proto\x1a\x14logger/options.proto\"\xec\x03\n\x11\x43reateRoomRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0broom_preset\x18\x0c \x01(\t\x12\x15\n\rempty_timeout\x18\x02 \x01(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\n \x01(\r\x12\x18\n\x10max_participants\x18\x03 \x01(\r\x12\x1a\n\x07node_id\x18\x04 \x01(\tB\t\xbaP\x06nodeID\x12\x36\n\x08metadata\x18\x05 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x32\n\x04tags\x18\x0f \x03(\x0b\x32$.livekit.CreateRoomRequest.TagsEntry\x12#\n\x06\x65gress\x18\x06 \x01(\x0b\x32\x13.livekit.RoomEgress\x12\x19\n\x11min_playout_delay\x18\x07 \x01(\r\x12\x19\n\x11max_playout_delay\x18\x08 \x01(\r\x12\x14\n\x0csync_streams\x18\t \x01(\x08\x12\x16\n\x0ereplay_enabled\x18\r \x01(\x08\x12*\n\x06\x61gents\x18\x0e \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9e\x01\n\nRoomEgress\x12\x31\n\x04room\x18\x01 \x01(\x0b\x32#.livekit.RoomCompositeEgressRequest\x12\x33\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x1e.livekit.AutoParticipantEgress\x12(\n\x06tracks\x18\x02 \x01(\x0b\x32\x18.livekit.AutoTrackEgress\";\n\tRoomAgent\x12.\n\ndispatches\x18\x01 \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\"!\n\x10ListRoomsRequest\x12\r\n\x05names\x18\x01 \x03(\t\"1\n\x11ListRoomsResponse\x12\x1c\n\x05rooms\x18\x01 \x03(\x0b\x32\r.livekit.Room\"!\n\x11\x44\x65leteRoomRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\"\x14\n\x12\x44\x65leteRoomResponse\"\'\n\x17ListParticipantsRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\"J\n\x18ListParticipantsResponse\x12.\n\x0cparticipants\x18\x01 \x03(\x0b\x32\x18.livekit.ParticipantInfo\"R\n\x17RoomParticipantIdentity\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x17\n\x0frevoke_token_ts\x18\x03 \x01(\x03\"\x1b\n\x19RemoveParticipantResponse\"X\n\x14MuteRoomTrackRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x11\n\ttrack_sid\x18\x03 \x01(\t\x12\r\n\x05muted\x18\x04 \x01(\x08\":\n\x15MuteRoomTrackResponse\x12!\n\x05track\x18\x01 \x01(\x0b\x32\x12.livekit.TrackInfo\"\xfa\x02\n\x18UpdateParticipantRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x36\n\x08metadata\x18\x03 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x32\n\npermission\x18\x04 \x01(\x0b\x32\x1e.livekit.ParticipantPermission\x12\x32\n\x04name\x18\x05 \x01(\tB$\xb2P\x1e\xc0P\x01\x12k\n\nattributes\x18\x06 \x03(\x0b\x32\x31.livekit.UpdateParticipantRequest.AttributesEntryB$\xb2P\x1e\xc0P\x01\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x9b\x01\n\x1aUpdateSubscriptionsRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x12\n\ntrack_sids\x18\x03 \x03(\t\x12\x11\n\tsubscribe\x18\x04 \x01(\x08\x12\x36\n\x12participant_tracks\x18\x05 \x03(\x0b\x32\x1a.livekit.ParticipantTracks\"\x1d\n\x1bUpdateSubscriptionsResponse\"\xc0\x01\n\x0fSendDataRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\x12&\n\x04kind\x18\x03 \x01(\x0e\x32\x18.livekit.DataPacket.Kind\x12\x1c\n\x10\x64\x65stination_sids\x18\x04 \x03(\tB\x02\x18\x01\x12\x1e\n\x16\x64\x65stination_identities\x18\x06 \x03(\t\x12\x12\n\x05topic\x18\x05 \x01(\tH\x00\x88\x01\x01\x12\r\n\x05nonce\x18\x07 \x01(\x0c\x42\x08\n\x06_topic\"\x12\n\x10SendDataResponse\"a\n\x19UpdateRoomMetadataRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x36\n\x08metadata\x18\x02 \x01(\tB$\xb2P\x1e\xc0P\x01\"\xa3\x03\n\x11RoomConfiguration\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rempty_timeout\x18\x02 \x01(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\x03 \x01(\r\x12\x18\n\x10max_participants\x18\x04 \x01(\r\x12\x36\n\x08metadata\x18\x0b \x01(\tB$\xb2P\x1e\xc0P\x01\x12#\n\x06\x65gress\x18\x05 \x01(\x0b\x32\x13.livekit.RoomEgress\x12\x19\n\x11min_playout_delay\x18\x07 \x01(\r\x12\x19\n\x11max_playout_delay\x18\x08 \x01(\r\x12\x14\n\x0csync_streams\x18\t \x01(\x08\x12*\n\x06\x61gents\x18\n \x03(\x0b\x32\x1a.livekit.RoomAgentDispatch\x12\x32\n\x04tags\x18\x0c \x03(\x0b\x32$.livekit.RoomConfiguration.TagsEntry\x1a+\n\tTagsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"U\n\x19\x46orwardParticipantRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x18\n\x10\x64\x65stination_room\x18\x03 \x01(\t\"\x1c\n\x1a\x46orwardParticipantResponse\"R\n\x16MoveParticipantRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x10\n\x08identity\x18\x02 \x01(\t\x12\x18\n\x10\x64\x65stination_room\x18\x03 \x01(\t\"\x19\n\x17MoveParticipantResponse\"}\n\x11PerformRpcRequest\x12\x0c\n\x04room\x18\x01 \x01(\t\x12\x1c\n\x14\x64\x65stination_identity\x18\x02 \x01(\t\x12\x0e\n\x06method\x18\x03 \x01(\t\x12\x0f\n\x07payload\x18\x04 \x01(\t\x12\x1b\n\x13response_timeout_ms\x18\x05 \x01(\r\"%\n\x12PerformRpcResponse\x12\x0f\n\x07payload\x18\x01 \x01(\t2\xe2\x08\n\x0bRoomService\x12\x37\n\nCreateRoom\x12\x1a.livekit.CreateRoomRequest\x1a\r.livekit.Room\x12\x42\n\tListRooms\x12\x19.livekit.ListRoomsRequest\x1a\x1a.livekit.ListRoomsResponse\x12\x45\n\nDeleteRoom\x12\x1a.livekit.DeleteRoomRequest\x1a\x1b.livekit.DeleteRoomResponse\x12W\n\x10ListParticipants\x12 .livekit.ListParticipantsRequest\x1a!.livekit.ListParticipantsResponse\x12L\n\x0eGetParticipant\x12 .livekit.RoomParticipantIdentity\x1a\x18.livekit.ParticipantInfo\x12Y\n\x11RemoveParticipant\x12 .livekit.RoomParticipantIdentity\x1a\".livekit.RemoveParticipantResponse\x12S\n\x12MutePublishedTrack\x12\x1d.livekit.MuteRoomTrackRequest\x1a\x1e.livekit.MuteRoomTrackResponse\x12P\n\x11UpdateParticipant\x12!.livekit.UpdateParticipantRequest\x1a\x18.livekit.ParticipantInfo\x12`\n\x13UpdateSubscriptions\x12#.livekit.UpdateSubscriptionsRequest\x1a$.livekit.UpdateSubscriptionsResponse\x12?\n\x08SendData\x12\x18.livekit.SendDataRequest\x1a\x19.livekit.SendDataResponse\x12G\n\x12UpdateRoomMetadata\x12\".livekit.UpdateRoomMetadataRequest\x1a\r.livekit.Room\x12]\n\x12\x46orwardParticipant\x12\".livekit.ForwardParticipantRequest\x1a#.livekit.ForwardParticipantResponse\x12T\n\x0fMoveParticipant\x12\x1f.livekit.MoveParticipantRequest\x1a .livekit.MoveParticipantResponse\x12\x45\n\nPerformRpc\x12\x1a.livekit.PerformRpcRequest\x1a\x1b.livekit.PerformRpcResponseBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'room', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._options = None + _globals['_CREATEROOMREQUEST_TAGSENTRY']._loaded_options = None + _globals['_CREATEROOMREQUEST_TAGSENTRY']._serialized_options = b'8\001' + _globals['_CREATEROOMREQUEST'].fields_by_name['node_id']._loaded_options = None + _globals['_CREATEROOMREQUEST'].fields_by_name['node_id']._serialized_options = b'\272P\006nodeID' + _globals['_CREATEROOMREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_CREATEROOMREQUEST'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._loaded_options = None _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_SENDDATAREQUEST'].fields_by_name['destination_sids']._options = None + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['name']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['attributes']._loaded_options = None + _globals['_UPDATEPARTICIPANTREQUEST'].fields_by_name['attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_SENDDATAREQUEST'].fields_by_name['destination_sids']._loaded_options = None _globals['_SENDDATAREQUEST'].fields_by_name['destination_sids']._serialized_options = b'\030\001' - _globals['_CREATEROOMREQUEST']._serialized_start=76 - _globals['_CREATEROOMREQUEST']._serialized_end=333 - _globals['_ROOMEGRESS']._serialized_start=336 - _globals['_ROOMEGRESS']._serialized_end=494 - _globals['_LISTROOMSREQUEST']._serialized_start=496 - _globals['_LISTROOMSREQUEST']._serialized_end=529 - _globals['_LISTROOMSRESPONSE']._serialized_start=531 - _globals['_LISTROOMSRESPONSE']._serialized_end=580 - _globals['_DELETEROOMREQUEST']._serialized_start=582 - _globals['_DELETEROOMREQUEST']._serialized_end=615 - _globals['_DELETEROOMRESPONSE']._serialized_start=617 - _globals['_DELETEROOMRESPONSE']._serialized_end=637 - _globals['_LISTPARTICIPANTSREQUEST']._serialized_start=639 - _globals['_LISTPARTICIPANTSREQUEST']._serialized_end=678 - _globals['_LISTPARTICIPANTSRESPONSE']._serialized_start=680 - _globals['_LISTPARTICIPANTSRESPONSE']._serialized_end=754 - _globals['_ROOMPARTICIPANTIDENTITY']._serialized_start=756 - _globals['_ROOMPARTICIPANTIDENTITY']._serialized_end=813 - _globals['_REMOVEPARTICIPANTRESPONSE']._serialized_start=815 - _globals['_REMOVEPARTICIPANTRESPONSE']._serialized_end=842 - _globals['_MUTEROOMTRACKREQUEST']._serialized_start=844 - _globals['_MUTEROOMTRACKREQUEST']._serialized_end=932 - _globals['_MUTEROOMTRACKRESPONSE']._serialized_start=934 - _globals['_MUTEROOMTRACKRESPONSE']._serialized_end=992 - _globals['_UPDATEPARTICIPANTREQUEST']._serialized_start=995 - _globals['_UPDATEPARTICIPANTREQUEST']._serialized_end=1259 - _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._serialized_start=1210 - _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._serialized_end=1259 - _globals['_UPDATESUBSCRIPTIONSREQUEST']._serialized_start=1262 - _globals['_UPDATESUBSCRIPTIONSREQUEST']._serialized_end=1417 - _globals['_UPDATESUBSCRIPTIONSRESPONSE']._serialized_start=1419 - _globals['_UPDATESUBSCRIPTIONSRESPONSE']._serialized_end=1448 - _globals['_SENDDATAREQUEST']._serialized_start=1451 - _globals['_SENDDATAREQUEST']._serialized_end=1628 - _globals['_SENDDATARESPONSE']._serialized_start=1630 - _globals['_SENDDATARESPONSE']._serialized_end=1648 - _globals['_UPDATEROOMMETADATAREQUEST']._serialized_start=1650 - _globals['_UPDATEROOMMETADATAREQUEST']._serialized_end=1709 - _globals['_ROOMSERVICE']._serialized_start=1712 - _globals['_ROOMSERVICE']._serialized_end=2582 + _globals['_UPDATEROOMMETADATAREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_UPDATEROOMMETADATAREQUEST'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_ROOMCONFIGURATION_TAGSENTRY']._loaded_options = None + _globals['_ROOMCONFIGURATION_TAGSENTRY']._serialized_options = b'8\001' + _globals['_ROOMCONFIGURATION'].fields_by_name['metadata']._loaded_options = None + _globals['_ROOMCONFIGURATION'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATEROOMREQUEST']._serialized_start=128 + _globals['_CREATEROOMREQUEST']._serialized_end=620 + _globals['_CREATEROOMREQUEST_TAGSENTRY']._serialized_start=577 + _globals['_CREATEROOMREQUEST_TAGSENTRY']._serialized_end=620 + _globals['_ROOMEGRESS']._serialized_start=623 + _globals['_ROOMEGRESS']._serialized_end=781 + _globals['_ROOMAGENT']._serialized_start=783 + _globals['_ROOMAGENT']._serialized_end=842 + _globals['_LISTROOMSREQUEST']._serialized_start=844 + _globals['_LISTROOMSREQUEST']._serialized_end=877 + _globals['_LISTROOMSRESPONSE']._serialized_start=879 + _globals['_LISTROOMSRESPONSE']._serialized_end=928 + _globals['_DELETEROOMREQUEST']._serialized_start=930 + _globals['_DELETEROOMREQUEST']._serialized_end=963 + _globals['_DELETEROOMRESPONSE']._serialized_start=965 + _globals['_DELETEROOMRESPONSE']._serialized_end=985 + _globals['_LISTPARTICIPANTSREQUEST']._serialized_start=987 + _globals['_LISTPARTICIPANTSREQUEST']._serialized_end=1026 + _globals['_LISTPARTICIPANTSRESPONSE']._serialized_start=1028 + _globals['_LISTPARTICIPANTSRESPONSE']._serialized_end=1102 + _globals['_ROOMPARTICIPANTIDENTITY']._serialized_start=1104 + _globals['_ROOMPARTICIPANTIDENTITY']._serialized_end=1186 + _globals['_REMOVEPARTICIPANTRESPONSE']._serialized_start=1188 + _globals['_REMOVEPARTICIPANTRESPONSE']._serialized_end=1215 + _globals['_MUTEROOMTRACKREQUEST']._serialized_start=1217 + _globals['_MUTEROOMTRACKREQUEST']._serialized_end=1305 + _globals['_MUTEROOMTRACKRESPONSE']._serialized_start=1307 + _globals['_MUTEROOMTRACKRESPONSE']._serialized_end=1365 + _globals['_UPDATEPARTICIPANTREQUEST']._serialized_start=1368 + _globals['_UPDATEPARTICIPANTREQUEST']._serialized_end=1746 + _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._serialized_start=1697 + _globals['_UPDATEPARTICIPANTREQUEST_ATTRIBUTESENTRY']._serialized_end=1746 + _globals['_UPDATESUBSCRIPTIONSREQUEST']._serialized_start=1749 + _globals['_UPDATESUBSCRIPTIONSREQUEST']._serialized_end=1904 + _globals['_UPDATESUBSCRIPTIONSRESPONSE']._serialized_start=1906 + _globals['_UPDATESUBSCRIPTIONSRESPONSE']._serialized_end=1935 + _globals['_SENDDATAREQUEST']._serialized_start=1938 + _globals['_SENDDATAREQUEST']._serialized_end=2130 + _globals['_SENDDATARESPONSE']._serialized_start=2132 + _globals['_SENDDATARESPONSE']._serialized_end=2150 + _globals['_UPDATEROOMMETADATAREQUEST']._serialized_start=2152 + _globals['_UPDATEROOMMETADATAREQUEST']._serialized_end=2249 + _globals['_ROOMCONFIGURATION']._serialized_start=2252 + _globals['_ROOMCONFIGURATION']._serialized_end=2671 + _globals['_ROOMCONFIGURATION_TAGSENTRY']._serialized_start=577 + _globals['_ROOMCONFIGURATION_TAGSENTRY']._serialized_end=620 + _globals['_FORWARDPARTICIPANTREQUEST']._serialized_start=2673 + _globals['_FORWARDPARTICIPANTREQUEST']._serialized_end=2758 + _globals['_FORWARDPARTICIPANTRESPONSE']._serialized_start=2760 + _globals['_FORWARDPARTICIPANTRESPONSE']._serialized_end=2788 + _globals['_MOVEPARTICIPANTREQUEST']._serialized_start=2790 + _globals['_MOVEPARTICIPANTREQUEST']._serialized_end=2872 + _globals['_MOVEPARTICIPANTRESPONSE']._serialized_start=2874 + _globals['_MOVEPARTICIPANTRESPONSE']._serialized_end=2899 + _globals['_PERFORMRPCREQUEST']._serialized_start=2901 + _globals['_PERFORMRPCREQUEST']._serialized_end=3026 + _globals['_PERFORMRPCRESPONSE']._serialized_start=3028 + _globals['_PERFORMRPCRESPONSE']._serialized_end=3065 + _globals['_ROOMSERVICE']._serialized_start=3068 + _globals['_ROOMSERVICE']._serialized_end=4190 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/room.pyi b/livekit-protocol/livekit/protocol/room.pyi index 2ba90dd9..5244f3e4 100644 --- a/livekit-protocol/livekit/protocol/room.pyi +++ b/livekit-protocol/livekit/protocol/room.pyi @@ -1,5 +1,7 @@ from . import models as _models from . import egress as _egress +from . import agent_dispatch as _agent_dispatch +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf import descriptor as _descriptor from google.protobuf import message as _message @@ -8,28 +10,43 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor class CreateRoomRequest(_message.Message): - __slots__ = ("name", "empty_timeout", "departure_timeout", "max_participants", "node_id", "metadata", "egress", "min_playout_delay", "max_playout_delay", "sync_streams") + __slots__ = ("name", "room_preset", "empty_timeout", "departure_timeout", "max_participants", "node_id", "metadata", "tags", "egress", "min_playout_delay", "max_playout_delay", "sync_streams", "replay_enabled", "agents") + class TagsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_PRESET_FIELD_NUMBER: _ClassVar[int] EMPTY_TIMEOUT_FIELD_NUMBER: _ClassVar[int] DEPARTURE_TIMEOUT_FIELD_NUMBER: _ClassVar[int] MAX_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] NODE_ID_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] + TAGS_FIELD_NUMBER: _ClassVar[int] EGRESS_FIELD_NUMBER: _ClassVar[int] MIN_PLAYOUT_DELAY_FIELD_NUMBER: _ClassVar[int] MAX_PLAYOUT_DELAY_FIELD_NUMBER: _ClassVar[int] SYNC_STREAMS_FIELD_NUMBER: _ClassVar[int] + REPLAY_ENABLED_FIELD_NUMBER: _ClassVar[int] + AGENTS_FIELD_NUMBER: _ClassVar[int] name: str + room_preset: str empty_timeout: int departure_timeout: int max_participants: int node_id: str metadata: str + tags: _containers.ScalarMap[str, str] egress: RoomEgress min_playout_delay: int max_playout_delay: int sync_streams: bool - def __init__(self, name: _Optional[str] = ..., empty_timeout: _Optional[int] = ..., departure_timeout: _Optional[int] = ..., max_participants: _Optional[int] = ..., node_id: _Optional[str] = ..., metadata: _Optional[str] = ..., egress: _Optional[_Union[RoomEgress, _Mapping]] = ..., min_playout_delay: _Optional[int] = ..., max_playout_delay: _Optional[int] = ..., sync_streams: bool = ...) -> None: ... + replay_enabled: bool + agents: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + def __init__(self, name: _Optional[str] = ..., room_preset: _Optional[str] = ..., empty_timeout: _Optional[int] = ..., departure_timeout: _Optional[int] = ..., max_participants: _Optional[int] = ..., node_id: _Optional[str] = ..., metadata: _Optional[str] = ..., tags: _Optional[_Mapping[str, str]] = ..., egress: _Optional[_Union[RoomEgress, _Mapping]] = ..., min_playout_delay: _Optional[int] = ..., max_playout_delay: _Optional[int] = ..., sync_streams: bool = ..., replay_enabled: bool = ..., agents: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ...) -> None: ... class RoomEgress(_message.Message): __slots__ = ("room", "participant", "tracks") @@ -41,6 +58,12 @@ class RoomEgress(_message.Message): tracks: _egress.AutoTrackEgress def __init__(self, room: _Optional[_Union[_egress.RoomCompositeEgressRequest, _Mapping]] = ..., participant: _Optional[_Union[_egress.AutoParticipantEgress, _Mapping]] = ..., tracks: _Optional[_Union[_egress.AutoTrackEgress, _Mapping]] = ...) -> None: ... +class RoomAgent(_message.Message): + __slots__ = ("dispatches",) + DISPATCHES_FIELD_NUMBER: _ClassVar[int] + dispatches: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + def __init__(self, dispatches: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ...) -> None: ... + class ListRoomsRequest(_message.Message): __slots__ = ("names",) NAMES_FIELD_NUMBER: _ClassVar[int] @@ -76,12 +99,14 @@ class ListParticipantsResponse(_message.Message): def __init__(self, participants: _Optional[_Iterable[_Union[_models.ParticipantInfo, _Mapping]]] = ...) -> None: ... class RoomParticipantIdentity(_message.Message): - __slots__ = ("room", "identity") + __slots__ = ("room", "identity", "revoke_token_ts") ROOM_FIELD_NUMBER: _ClassVar[int] IDENTITY_FIELD_NUMBER: _ClassVar[int] + REVOKE_TOKEN_TS_FIELD_NUMBER: _ClassVar[int] room: str identity: str - def __init__(self, room: _Optional[str] = ..., identity: _Optional[str] = ...) -> None: ... + revoke_token_ts: int + def __init__(self, room: _Optional[str] = ..., identity: _Optional[str] = ..., revoke_token_ts: _Optional[int] = ...) -> None: ... class RemoveParticipantResponse(_message.Message): __slots__ = () @@ -147,20 +172,22 @@ class UpdateSubscriptionsResponse(_message.Message): def __init__(self) -> None: ... class SendDataRequest(_message.Message): - __slots__ = ("room", "data", "kind", "destination_sids", "destination_identities", "topic") + __slots__ = ("room", "data", "kind", "destination_sids", "destination_identities", "topic", "nonce") ROOM_FIELD_NUMBER: _ClassVar[int] DATA_FIELD_NUMBER: _ClassVar[int] KIND_FIELD_NUMBER: _ClassVar[int] DESTINATION_SIDS_FIELD_NUMBER: _ClassVar[int] DESTINATION_IDENTITIES_FIELD_NUMBER: _ClassVar[int] TOPIC_FIELD_NUMBER: _ClassVar[int] + NONCE_FIELD_NUMBER: _ClassVar[int] room: str data: bytes kind: _models.DataPacket.Kind destination_sids: _containers.RepeatedScalarFieldContainer[str] destination_identities: _containers.RepeatedScalarFieldContainer[str] topic: str - def __init__(self, room: _Optional[str] = ..., data: _Optional[bytes] = ..., kind: _Optional[_Union[_models.DataPacket.Kind, str]] = ..., destination_sids: _Optional[_Iterable[str]] = ..., destination_identities: _Optional[_Iterable[str]] = ..., topic: _Optional[str] = ...) -> None: ... + nonce: bytes + def __init__(self, room: _Optional[str] = ..., data: _Optional[bytes] = ..., kind: _Optional[_Union[_models.DataPacket.Kind, str]] = ..., destination_sids: _Optional[_Iterable[str]] = ..., destination_identities: _Optional[_Iterable[str]] = ..., topic: _Optional[str] = ..., nonce: _Optional[bytes] = ...) -> None: ... class SendDataResponse(_message.Message): __slots__ = () @@ -173,3 +200,84 @@ class UpdateRoomMetadataRequest(_message.Message): room: str metadata: str def __init__(self, room: _Optional[str] = ..., metadata: _Optional[str] = ...) -> None: ... + +class RoomConfiguration(_message.Message): + __slots__ = ("name", "empty_timeout", "departure_timeout", "max_participants", "metadata", "egress", "min_playout_delay", "max_playout_delay", "sync_streams", "agents", "tags") + class TagsEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + NAME_FIELD_NUMBER: _ClassVar[int] + EMPTY_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + DEPARTURE_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + MAX_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + EGRESS_FIELD_NUMBER: _ClassVar[int] + MIN_PLAYOUT_DELAY_FIELD_NUMBER: _ClassVar[int] + MAX_PLAYOUT_DELAY_FIELD_NUMBER: _ClassVar[int] + SYNC_STREAMS_FIELD_NUMBER: _ClassVar[int] + AGENTS_FIELD_NUMBER: _ClassVar[int] + TAGS_FIELD_NUMBER: _ClassVar[int] + name: str + empty_timeout: int + departure_timeout: int + max_participants: int + metadata: str + egress: RoomEgress + min_playout_delay: int + max_playout_delay: int + sync_streams: bool + agents: _containers.RepeatedCompositeFieldContainer[_agent_dispatch.RoomAgentDispatch] + tags: _containers.ScalarMap[str, str] + def __init__(self, name: _Optional[str] = ..., empty_timeout: _Optional[int] = ..., departure_timeout: _Optional[int] = ..., max_participants: _Optional[int] = ..., metadata: _Optional[str] = ..., egress: _Optional[_Union[RoomEgress, _Mapping]] = ..., min_playout_delay: _Optional[int] = ..., max_playout_delay: _Optional[int] = ..., sync_streams: bool = ..., agents: _Optional[_Iterable[_Union[_agent_dispatch.RoomAgentDispatch, _Mapping]]] = ..., tags: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class ForwardParticipantRequest(_message.Message): + __slots__ = ("room", "identity", "destination_room") + ROOM_FIELD_NUMBER: _ClassVar[int] + IDENTITY_FIELD_NUMBER: _ClassVar[int] + DESTINATION_ROOM_FIELD_NUMBER: _ClassVar[int] + room: str + identity: str + destination_room: str + def __init__(self, room: _Optional[str] = ..., identity: _Optional[str] = ..., destination_room: _Optional[str] = ...) -> None: ... + +class ForwardParticipantResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class MoveParticipantRequest(_message.Message): + __slots__ = ("room", "identity", "destination_room") + ROOM_FIELD_NUMBER: _ClassVar[int] + IDENTITY_FIELD_NUMBER: _ClassVar[int] + DESTINATION_ROOM_FIELD_NUMBER: _ClassVar[int] + room: str + identity: str + destination_room: str + def __init__(self, room: _Optional[str] = ..., identity: _Optional[str] = ..., destination_room: _Optional[str] = ...) -> None: ... + +class MoveParticipantResponse(_message.Message): + __slots__ = () + def __init__(self) -> None: ... + +class PerformRpcRequest(_message.Message): + __slots__ = ("room", "destination_identity", "method", "payload", "response_timeout_ms") + ROOM_FIELD_NUMBER: _ClassVar[int] + DESTINATION_IDENTITY_FIELD_NUMBER: _ClassVar[int] + METHOD_FIELD_NUMBER: _ClassVar[int] + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + RESPONSE_TIMEOUT_MS_FIELD_NUMBER: _ClassVar[int] + room: str + destination_identity: str + method: str + payload: str + response_timeout_ms: int + def __init__(self, room: _Optional[str] = ..., destination_identity: _Optional[str] = ..., method: _Optional[str] = ..., payload: _Optional[str] = ..., response_timeout_ms: _Optional[int] = ...) -> None: ... + +class PerformRpcResponse(_message.Message): + __slots__ = ("payload",) + PAYLOAD_FIELD_NUMBER: _ClassVar[int] + payload: str + def __init__(self, payload: _Optional[str] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/rtc.py b/livekit-protocol/livekit/protocol/rtc.py new file mode 100644 index 00000000..8564994e --- /dev/null +++ b/livekit-protocol/livekit/protocol/rtc.py @@ -0,0 +1,225 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: livekit_rtc.proto +# Protobuf Python Version: 5.29.3 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_rtc.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import models as _models_ +from .logger_pb import options as logger_dot_options__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11livekit_rtc.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14logger/options.proto\"\xae\t\n\rSignalRequest\x12,\n\x05offer\x18\x01 \x01(\x0b\x32\x1b.livekit.SessionDescriptionH\x00\x12-\n\x06\x61nswer\x18\x02 \x01(\x0b\x32\x1b.livekit.SessionDescriptionH\x00\x12*\n\x07trickle\x18\x03 \x01(\x0b\x32\x17.livekit.TrickleRequestH\x00\x12-\n\tadd_track\x18\x04 \x01(\x0b\x32\x18.livekit.AddTrackRequestH\x00\x12)\n\x04mute\x18\x05 \x01(\x0b\x32\x19.livekit.MuteTrackRequestH\x00\x12\x33\n\x0csubscription\x18\x06 \x01(\x0b\x32\x1b.livekit.UpdateSubscriptionH\x00\x12\x35\n\rtrack_setting\x18\x07 \x01(\x0b\x32\x1c.livekit.UpdateTrackSettingsH\x00\x12&\n\x05leave\x18\x08 \x01(\x0b\x32\x15.livekit.LeaveRequestH\x00\x12\x37\n\rupdate_layers\x18\n \x01(\x0b\x32\x1a.livekit.UpdateVideoLayersB\x02\x18\x01H\x00\x12\x42\n\x17subscription_permission\x18\x0b \x01(\x0b\x32\x1f.livekit.SubscriptionPermissionH\x00\x12(\n\nsync_state\x18\x0c \x01(\x0b\x32\x12.livekit.SyncStateH\x00\x12-\n\x08simulate\x18\r \x01(\x0b\x32\x19.livekit.SimulateScenarioH\x00\x12\x0e\n\x04ping\x18\x0e \x01(\x03H\x00\x12=\n\x0fupdate_metadata\x18\x0f \x01(\x0b\x32\".livekit.UpdateParticipantMetadataH\x00\x12!\n\x08ping_req\x18\x10 \x01(\x0b\x32\r.livekit.PingH\x00\x12<\n\x12update_audio_track\x18\x11 \x01(\x0b\x32\x1e.livekit.UpdateLocalAudioTrackH\x00\x12<\n\x12update_video_track\x18\x12 \x01(\x0b\x32\x1e.livekit.UpdateLocalVideoTrackH\x00\x12\x46\n\x1apublish_data_track_request\x18\x13 \x01(\x0b\x32 .livekit.PublishDataTrackRequestH\x00\x12J\n\x1cunpublish_data_track_request\x18\x14 \x01(\x0b\x32\".livekit.UnpublishDataTrackRequestH\x00\x12\x43\n\x18update_data_subscription\x18\x15 \x01(\x0b\x32\x1f.livekit.UpdateDataSubscriptionH\x00\x12@\n\x17store_data_blob_request\x18\x16 \x01(\x0b\x32\x1d.livekit.StoreDataBlobRequestH\x00\x12<\n\x15get_data_blob_request\x18\x17 \x01(\x0b\x32\x1b.livekit.GetDataBlobRequestH\x00\x42\t\n\x07message\"\x9a\r\n\x0eSignalResponse\x12%\n\x04join\x18\x01 \x01(\x0b\x32\x15.livekit.JoinResponseH\x00\x12-\n\x06\x61nswer\x18\x02 \x01(\x0b\x32\x1b.livekit.SessionDescriptionH\x00\x12,\n\x05offer\x18\x03 \x01(\x0b\x32\x1b.livekit.SessionDescriptionH\x00\x12*\n\x07trickle\x18\x04 \x01(\x0b\x32\x17.livekit.TrickleRequestH\x00\x12,\n\x06update\x18\x05 \x01(\x0b\x32\x1a.livekit.ParticipantUpdateH\x00\x12:\n\x0ftrack_published\x18\x06 \x01(\x0b\x32\x1f.livekit.TrackPublishedResponseH\x00\x12&\n\x05leave\x18\x08 \x01(\x0b\x32\x15.livekit.LeaveRequestH\x00\x12)\n\x04mute\x18\t \x01(\x0b\x32\x19.livekit.MuteTrackRequestH\x00\x12\x34\n\x10speakers_changed\x18\n \x01(\x0b\x32\x18.livekit.SpeakersChangedH\x00\x12*\n\x0broom_update\x18\x0b \x01(\x0b\x32\x13.livekit.RoomUpdateH\x00\x12>\n\x12\x63onnection_quality\x18\x0c \x01(\x0b\x32 .livekit.ConnectionQualityUpdateH\x00\x12\x39\n\x13stream_state_update\x18\r \x01(\x0b\x32\x1a.livekit.StreamStateUpdateH\x00\x12\x45\n\x19subscribed_quality_update\x18\x0e \x01(\x0b\x32 .livekit.SubscribedQualityUpdateH\x00\x12O\n\x1esubscription_permission_update\x18\x0f \x01(\x0b\x32%.livekit.SubscriptionPermissionUpdateH\x00\x12\x17\n\rrefresh_token\x18\x10 \x01(\tH\x00\x12>\n\x11track_unpublished\x18\x11 \x01(\x0b\x32!.livekit.TrackUnpublishedResponseH\x00\x12\x0e\n\x04pong\x18\x12 \x01(\x03H\x00\x12/\n\treconnect\x18\x13 \x01(\x0b\x32\x1a.livekit.ReconnectResponseH\x00\x12\"\n\tpong_resp\x18\x14 \x01(\x0b\x32\r.livekit.PongH\x00\x12>\n\x15subscription_response\x18\x15 \x01(\x0b\x32\x1d.livekit.SubscriptionResponseH\x00\x12\x34\n\x10request_response\x18\x16 \x01(\x0b\x32\x18.livekit.RequestResponseH\x00\x12\x34\n\x10track_subscribed\x18\x17 \x01(\x0b\x32\x18.livekit.TrackSubscribedH\x00\x12\x30\n\nroom_moved\x18\x18 \x01(\x0b\x32\x1a.livekit.RoomMovedResponseH\x00\x12G\n\x1amedia_sections_requirement\x18\x19 \x01(\x0b\x32!.livekit.MediaSectionsRequirementH\x00\x12L\n\x1dsubscribed_audio_codec_update\x18\x1a \x01(\x0b\x32#.livekit.SubscribedAudioCodecUpdateH\x00\x12H\n\x1bpublish_data_track_response\x18\x1b \x01(\x0b\x32!.livekit.PublishDataTrackResponseH\x00\x12L\n\x1dunpublish_data_track_response\x18\x1c \x01(\x0b\x32#.livekit.UnpublishDataTrackResponseH\x00\x12L\n\x1d\x64\x61ta_track_subscriber_handles\x18\x1d \x01(\x0b\x32#.livekit.DataTrackSubscriberHandlesH\x00\x12\x42\n\x18store_data_blob_response\x18\x1e \x01(\x0b\x32\x1e.livekit.StoreDataBlobResponseH\x00\x12>\n\x16get_data_blob_response\x18\x1f \x01(\x0b\x32\x1c.livekit.GetDataBlobResponseH\x00\x42\t\n\x07message\"\x85\x01\n\x0eSimulcastCodec\x12\r\n\x05\x63odec\x18\x01 \x01(\t\x12\x0b\n\x03\x63id\x18\x02 \x01(\t\x12#\n\x06layers\x18\x04 \x03(\x0b\x32\x13.livekit.VideoLayer\x12\x32\n\x10video_layer_mode\x18\x05 \x01(\x0e\x32\x18.livekit.VideoLayer.Mode\"\xb4\x04\n\x0f\x41\x64\x64TrackRequest\x12\x0b\n\x03\x63id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12 \n\x04type\x18\x03 \x01(\x0e\x32\x12.livekit.TrackType\x12\r\n\x05width\x18\x04 \x01(\r\x12\x0e\n\x06height\x18\x05 \x01(\r\x12\r\n\x05muted\x18\x06 \x01(\x08\x12\x17\n\x0b\x64isable_dtx\x18\x07 \x01(\x08\x42\x02\x18\x01\x12$\n\x06source\x18\x08 \x01(\x0e\x32\x14.livekit.TrackSource\x12#\n\x06layers\x18\t \x03(\x0b\x32\x13.livekit.VideoLayer\x12\x31\n\x10simulcast_codecs\x18\n \x03(\x0b\x32\x17.livekit.SimulcastCodec\x12\x0b\n\x03sid\x18\x0b \x01(\t\x12\x12\n\x06stereo\x18\x0c \x01(\x08\x42\x02\x18\x01\x12\x13\n\x0b\x64isable_red\x18\r \x01(\x08\x12,\n\nencryption\x18\x0e \x01(\x0e\x32\x18.livekit.Encryption.Type\x12\x0e\n\x06stream\x18\x0f \x01(\t\x12\x37\n\x13\x62\x61\x63kup_codec_policy\x18\x10 \x01(\x0e\x32\x1a.livekit.BackupCodecPolicy\x12\x32\n\x0e\x61udio_features\x18\x11 \x03(\x0e\x32\x1a.livekit.AudioTrackFeature\x12>\n\x17packet_trailer_features\x18\x12 \x03(\x0e\x32\x1d.livekit.PacketTrailerFeature\"\xf6\x01\n\x17PublishDataTrackRequest\x12\x12\n\npub_handle\x18\x01 \x01(\r\x12\x0c\n\x04name\x18\x02 \x01(\t\x12,\n\nencryption\x18\x03 \x01(\x0e\x32\x18.livekit.Encryption.Type\x12<\n\x0e\x66rame_encoding\x18\x04 \x01(\x0b\x32\x1f.livekit.DataTrackFrameEncodingH\x00\x88\x01\x01\x12/\n\x06schema\x18\x05 \x01(\x0b\x32\x1a.livekit.DataTrackSchemaIdH\x01\x88\x01\x01\x42\x11\n\x0f_frame_encodingB\t\n\x07_schema\"@\n\x18PublishDataTrackResponse\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.livekit.DataTrackInfo\"/\n\x19UnpublishDataTrackRequest\x12\x12\n\npub_handle\x18\x01 \x01(\r\"B\n\x1aUnpublishDataTrackResponse\x12$\n\x04info\x18\x01 \x01(\x0b\x32\x16.livekit.DataTrackInfo\"\xad\x02\n\x1a\x44\x61taTrackSubscriberHandles\x12H\n\x0bsub_handles\x18\x01 \x03(\x0b\x32\x33.livekit.DataTrackSubscriberHandles.SubHandlesEntry\x1aZ\n\x12PublishedDataTrack\x12\x1a\n\x12publisher_identity\x18\x01 \x01(\t\x12\x15\n\rpublisher_sid\x18\x02 \x01(\t\x12\x11\n\ttrack_sid\x18\x03 \x01(\t\x1ai\n\x0fSubHandlesEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12\x45\n\x05value\x18\x02 \x01(\x0b\x32\x36.livekit.DataTrackSubscriberHandles.PublishedDataTrack:\x02\x38\x01\"]\n\x0eTrickleRequest\x12\x15\n\rcandidateInit\x18\x01 \x01(\t\x12%\n\x06target\x18\x02 \x01(\x0e\x32\x15.livekit.SignalTarget\x12\r\n\x05\x66inal\x18\x03 \x01(\x08\".\n\x10MuteTrackRequest\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\r\n\x05muted\x18\x02 \x01(\x08\"\x8b\x04\n\x0cJoinResponse\x12\x1b\n\x04room\x18\x01 \x01(\x0b\x32\r.livekit.Room\x12-\n\x0bparticipant\x18\x02 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x34\n\x12other_participants\x18\x03 \x03(\x0b\x32\x18.livekit.ParticipantInfo\x12\x16\n\x0eserver_version\x18\x04 \x01(\t\x12\'\n\x0bice_servers\x18\x05 \x03(\x0b\x32\x12.livekit.ICEServer\x12\x1a\n\x12subscriber_primary\x18\x06 \x01(\x08\x12\x17\n\x0f\x61lternative_url\x18\x07 \x01(\t\x12:\n\x14\x63lient_configuration\x18\x08 \x01(\x0b\x32\x1c.livekit.ClientConfiguration\x12\x15\n\rserver_region\x18\t \x01(\t\x12\x14\n\x0cping_timeout\x18\n \x01(\x05\x12\x15\n\rping_interval\x18\x0b \x01(\x05\x12(\n\x0bserver_info\x18\x0c \x01(\x0b\x32\x13.livekit.ServerInfo\x12\x13\n\x0bsif_trailer\x18\r \x01(\x0c\x12.\n\x16\x65nabled_publish_codecs\x18\x0e \x03(\x0b\x32\x0e.livekit.Codec\x12\x14\n\x0c\x66\x61st_publish\x18\x0f \x01(\x08\"\xbc\x01\n\x11ReconnectResponse\x12\'\n\x0bice_servers\x18\x01 \x03(\x0b\x32\x12.livekit.ICEServer\x12:\n\x14\x63lient_configuration\x18\x02 \x01(\x0b\x32\x1c.livekit.ClientConfiguration\x12(\n\x0bserver_info\x18\x03 \x01(\x0b\x32\x13.livekit.ServerInfo\x12\x18\n\x10last_message_seq\x18\x04 \x01(\r\"H\n\x16TrackPublishedResponse\x12\x0b\n\x03\x63id\x18\x01 \x01(\t\x12!\n\x05track\x18\x02 \x01(\x0b\x32\x12.livekit.TrackInfo\"-\n\x18TrackUnpublishedResponse\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\"\xc9\x01\n\x12SessionDescription\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\x0b\n\x03sdp\x18\x02 \x01(\t\x12\n\n\x02id\x18\x03 \x01(\r\x12W\n\x0fmid_to_track_id\x18\x04 \x03(\x0b\x32-.livekit.SessionDescription.MidToTrackIdEntryB\x0f\xbaP\x0cmidToTrackID\x1a\x33\n\x11MidToTrackIdEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"C\n\x11ParticipantUpdate\x12.\n\x0cparticipants\x18\x01 \x03(\x0b\x32\x18.livekit.ParticipantInfo\"s\n\x12UpdateSubscription\x12\x12\n\ntrack_sids\x18\x01 \x03(\t\x12\x11\n\tsubscribe\x18\x02 \x01(\x08\x12\x36\n\x12participant_tracks\x18\x03 \x03(\x0b\x32\x1a.livekit.ParticipantTracks\"\xb9\x01\n\x16UpdateDataSubscription\x12\x37\n\x07updates\x18\x01 \x03(\x0b\x32&.livekit.UpdateDataSubscription.Update\x1a\x66\n\x06Update\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12\x11\n\tsubscribe\x18\x02 \x01(\x08\x12\x36\n\x07options\x18\x03 \x01(\x0b\x32%.livekit.DataTrackSubscriptionOptions\"Y\n\x14StoreDataBlobRequest\x12 \n\nrequest_id\x18\x01 \x01(\rB\x0c\xbaP\trequestID\x12\x1f\n\x04\x62lob\x18\x02 \x01(\x0b\x32\x11.livekit.DataBlob\"\\\n\x15StoreDataBlobResponse\x12 \n\nrequest_id\x18\x01 \x01(\rB\x0c\xbaP\trequestID\x12!\n\x03key\x18\x02 \x01(\x0b\x32\x14.livekit.DataBlobKey\"w\n\x12GetDataBlobRequest\x12 \n\nrequest_id\x18\x01 \x01(\rB\x0c\xbaP\trequestID\x12\x1c\n\x14participant_identity\x18\x02 \x01(\t\x12!\n\x03key\x18\x03 \x01(\x0b\x32\x14.livekit.DataBlobKey\"X\n\x13GetDataBlobResponse\x12 \n\nrequest_id\x18\x01 \x01(\rB\x0c\xbaP\trequestID\x12\x1f\n\x04\x62lob\x18\x02 \x01(\x0b\x32\x11.livekit.DataBlob\"\xa1\x01\n\x13UpdateTrackSettings\x12\x12\n\ntrack_sids\x18\x01 \x03(\t\x12\x10\n\x08\x64isabled\x18\x03 \x01(\x08\x12&\n\x07quality\x18\x04 \x01(\x0e\x32\x15.livekit.VideoQuality\x12\r\n\x05width\x18\x05 \x01(\r\x12\x0e\n\x06height\x18\x06 \x01(\r\x12\x0b\n\x03\x66ps\x18\x07 \x01(\r\x12\x10\n\x08priority\x18\x08 \x01(\r\"X\n\x15UpdateLocalAudioTrack\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12,\n\x08\x66\x65\x61tures\x18\x02 \x03(\x0e\x32\x1a.livekit.AudioTrackFeature\"I\n\x15UpdateLocalVideoTrack\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\"\xdd\x01\n\x0cLeaveRequest\x12\x15\n\rcan_reconnect\x18\x01 \x01(\x08\x12)\n\x06reason\x18\x02 \x01(\x0e\x32\x19.livekit.DisconnectReason\x12,\n\x06\x61\x63tion\x18\x03 \x01(\x0e\x32\x1c.livekit.LeaveRequest.Action\x12(\n\x07regions\x18\x04 \x01(\x0b\x32\x17.livekit.RegionSettings\"3\n\x06\x41\x63tion\x12\x0e\n\nDISCONNECT\x10\x00\x12\n\n\x06RESUME\x10\x01\x12\r\n\tRECONNECT\x10\x02\"O\n\x11UpdateVideoLayers\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12#\n\x06layers\x18\x02 \x03(\x0b\x32\x13.livekit.VideoLayer:\x02\x18\x01\"\xca\x02\n\x19UpdateParticipantMetadata\x12\x36\n\x08metadata\x18\x01 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x32\n\x04name\x18\x02 \x01(\tB$\xb2P\x1e\xc0P\x01\x12l\n\nattributes\x18\x03 \x03(\x0b\x32\x32.livekit.UpdateParticipantMetadata.AttributesEntryB$\xb2P\x1e\xc0P\x01\x12 \n\nrequest_id\x18\x04 \x01(\rB\x0c\xbaP\trequestID\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"I\n\tICEServer\x12\x0c\n\x04urls\x18\x01 \x03(\t\x12\x15\n\x08username\x18\x02 \x01(\tB\x03\xc0P\x01\x12\x17\n\ncredential\x18\x03 \x01(\tB\x03\xc0P\x02\"9\n\x0fSpeakersChanged\x12&\n\x08speakers\x18\x01 \x03(\x0b\x32\x14.livekit.SpeakerInfo\")\n\nRoomUpdate\x12\x1b\n\x04room\x18\x01 \x01(\x0b\x32\r.livekit.Room\"l\n\x15\x43onnectionQualityInfo\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12+\n\x07quality\x18\x02 \x01(\x0e\x32\x1a.livekit.ConnectionQuality\x12\r\n\x05score\x18\x03 \x01(\x02\"J\n\x17\x43onnectionQualityUpdate\x12/\n\x07updates\x18\x01 \x03(\x0b\x32\x1e.livekit.ConnectionQualityInfo\"b\n\x0fStreamStateInfo\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12#\n\x05state\x18\x03 \x01(\x0e\x32\x14.livekit.StreamState\"D\n\x11StreamStateUpdate\x12/\n\rstream_states\x18\x01 \x03(\x0b\x32\x18.livekit.StreamStateInfo\"L\n\x11SubscribedQuality\x12&\n\x07quality\x18\x01 \x01(\x0e\x32\x15.livekit.VideoQuality\x12\x0f\n\x07\x65nabled\x18\x02 \x01(\x08\"O\n\x0fSubscribedCodec\x12\r\n\x05\x63odec\x18\x01 \x01(\t\x12-\n\tqualities\x18\x02 \x03(\x0b\x32\x1a.livekit.SubscribedQuality\"\x9f\x01\n\x17SubscribedQualityUpdate\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12<\n\x14subscribed_qualities\x18\x02 \x03(\x0b\x32\x1a.livekit.SubscribedQualityB\x02\x18\x01\x12\x33\n\x11subscribed_codecs\x18\x03 \x03(\x0b\x32\x18.livekit.SubscribedCodec\"o\n\x1aSubscribedAudioCodecUpdate\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12>\n\x17subscribed_audio_codecs\x18\x02 \x03(\x0b\x32\x1d.livekit.SubscribedAudioCodec\"p\n\x0fTrackPermission\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x12\n\nall_tracks\x18\x02 \x01(\x08\x12\x12\n\ntrack_sids\x18\x03 \x03(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\"g\n\x16SubscriptionPermission\x12\x18\n\x10\x61ll_participants\x18\x01 \x01(\x08\x12\x33\n\x11track_permissions\x18\x02 \x03(\x0b\x32\x18.livekit.TrackPermission\"[\n\x1cSubscriptionPermissionUpdate\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x0f\n\x07\x61llowed\x18\x03 \x01(\x08\"\xa4\x01\n\x11RoomMovedResponse\x12\x1b\n\x04room\x18\x01 \x01(\x0b\x32\r.livekit.Room\x12\r\n\x05token\x18\x02 \x01(\t\x12-\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12\x34\n\x12other_participants\x18\x04 \x03(\x0b\x32\x18.livekit.ParticipantInfo\"\xa4\x03\n\tSyncState\x12+\n\x06\x61nswer\x18\x01 \x01(\x0b\x32\x1b.livekit.SessionDescription\x12\x31\n\x0csubscription\x18\x02 \x01(\x0b\x32\x1b.livekit.UpdateSubscription\x12\x37\n\x0epublish_tracks\x18\x03 \x03(\x0b\x32\x1f.livekit.TrackPublishedResponse\x12/\n\rdata_channels\x18\x04 \x03(\x0b\x32\x18.livekit.DataChannelInfo\x12*\n\x05offer\x18\x05 \x01(\x0b\x32\x1b.livekit.SessionDescription\x12\x1b\n\x13track_sids_disabled\x18\x06 \x03(\t\x12\x44\n\x1a\x64\x61tachannel_receive_states\x18\x07 \x03(\x0b\x32 .livekit.DataChannelReceiveState\x12>\n\x13publish_data_tracks\x18\x08 \x03(\x0b\x32!.livekit.PublishDataTrackResponse\"B\n\x17\x44\x61taChannelReceiveState\x12\x15\n\rpublisher_sid\x18\x01 \x01(\t\x12\x10\n\x08last_seq\x18\x02 \x01(\r\"S\n\x0f\x44\x61taChannelInfo\x12\r\n\x05label\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\r\x12%\n\x06target\x18\x03 \x01(\x0e\x32\x15.livekit.SignalTarget\"\xe0\x02\n\x10SimulateScenario\x12\x18\n\x0espeaker_update\x18\x01 \x01(\x05H\x00\x12\x16\n\x0cnode_failure\x18\x02 \x01(\x08H\x00\x12\x13\n\tmigration\x18\x03 \x01(\x08H\x00\x12\x16\n\x0cserver_leave\x18\x04 \x01(\x08H\x00\x12?\n\x19switch_candidate_protocol\x18\x05 \x01(\x0e\x32\x1a.livekit.CandidateProtocolH\x00\x12\x1e\n\x14subscriber_bandwidth\x18\x06 \x01(\x03H\x00\x12%\n\x1b\x64isconnect_signal_on_resume\x18\x07 \x01(\x08H\x00\x12\x31\n\'disconnect_signal_on_resume_no_messages\x18\x08 \x01(\x08H\x00\x12&\n\x1cleave_request_full_reconnect\x18\t \x01(\x08H\x00\x42\n\n\x08scenario\"&\n\x04Ping\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x0b\n\x03rtt\x18\x02 \x01(\x03\"6\n\x04Pong\x12\x1b\n\x13last_ping_timestamp\x18\x01 \x01(\x03\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\"6\n\x0eRegionSettings\x12$\n\x07regions\x18\x01 \x03(\x0b\x32\x13.livekit.RegionInfo\";\n\nRegionInfo\x12\x0e\n\x06region\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x10\n\x08\x64istance\x18\x03 \x01(\x03\"R\n\x14SubscriptionResponse\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\x12\'\n\x03\x65rr\x18\x02 \x01(\x0e\x32\x1a.livekit.SubscriptionError\"\xab\x06\n\x0fRequestResponse\x12 \n\nrequest_id\x18\x01 \x01(\rB\x0c\xbaP\trequestID\x12/\n\x06reason\x18\x02 \x01(\x0e\x32\x1f.livekit.RequestResponse.Reason\x12\x0f\n\x07message\x18\x03 \x01(\t\x12*\n\x07trickle\x18\x04 \x01(\x0b\x32\x17.livekit.TrickleRequestH\x00\x12-\n\tadd_track\x18\x05 \x01(\x0b\x32\x18.livekit.AddTrackRequestH\x00\x12)\n\x04mute\x18\x06 \x01(\x0b\x32\x19.livekit.MuteTrackRequestH\x00\x12=\n\x0fupdate_metadata\x18\x07 \x01(\x0b\x32\".livekit.UpdateParticipantMetadataH\x00\x12<\n\x12update_audio_track\x18\x08 \x01(\x0b\x32\x1e.livekit.UpdateLocalAudioTrackH\x00\x12<\n\x12update_video_track\x18\t \x01(\x0b\x32\x1e.livekit.UpdateLocalVideoTrackH\x00\x12>\n\x12publish_data_track\x18\n \x01(\x0b\x32 .livekit.PublishDataTrackRequestH\x00\x12\x42\n\x14unpublish_data_track\x18\x0b \x01(\x0b\x32\".livekit.UnpublishDataTrackRequestH\x00\"\xe3\x01\n\x06Reason\x12\x06\n\x02OK\x10\x00\x12\r\n\tNOT_FOUND\x10\x01\x12\x0f\n\x0bNOT_ALLOWED\x10\x02\x12\x12\n\x0eLIMIT_EXCEEDED\x10\x03\x12\n\n\x06QUEUED\x10\x04\x12\x14\n\x10UNSUPPORTED_TYPE\x10\x05\x12\x16\n\x12UNCLASSIFIED_ERROR\x10\x06\x12\x12\n\x0eINVALID_HANDLE\x10\x07\x12\x10\n\x0cINVALID_NAME\x10\x08\x12\x14\n\x10\x44UPLICATE_HANDLE\x10\t\x12\x12\n\x0e\x44UPLICATE_NAME\x10\n\x12\x13\n\x0fINVALID_REQUEST\x10\x0b\x42\t\n\x07request\"$\n\x0fTrackSubscribed\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\"\xe5\x01\n\x12\x43onnectionSettings\x12\x16\n\x0e\x61uto_subscribe\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x64\x61ptive_stream\x18\x02 \x01(\x08\x12#\n\x16subscriber_allow_pause\x18\x03 \x01(\x08H\x00\x88\x01\x01\x12\x18\n\x10\x64isable_ice_lite\x18\x04 \x01(\x08\x12&\n\x19\x61uto_subscribe_data_track\x18\x05 \x01(\x08H\x01\x88\x01\x01\x42\x19\n\x17_subscriber_allow_pauseB\x1c\n\x1a_auto_subscribe_data_track\"\xd2\x04\n\x0bJoinRequest\x12(\n\x0b\x63lient_info\x18\x01 \x01(\x0b\x32\x13.livekit.ClientInfo\x12\x38\n\x13\x63onnection_settings\x18\x02 \x01(\x0b\x32\x1b.livekit.ConnectionSettings\x12\x36\n\x08metadata\x18\x03 \x01(\tB$\xb2P\x1e\xc0P\x01\x12u\n\x16participant_attributes\x18\x04 \x03(\x0b\x32/.livekit.JoinRequest.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x34\n\x12\x61\x64\x64_track_requests\x18\x05 \x03(\x0b\x32\x18.livekit.AddTrackRequest\x12\x34\n\x0fpublisher_offer\x18\x06 \x01(\x0b\x32\x1b.livekit.SessionDescription\x12\x11\n\treconnect\x18\x07 \x01(\x08\x12\x32\n\x10reconnect_reason\x18\x08 \x01(\x0e\x32\x18.livekit.ReconnectReason\x12\x17\n\x0fparticipant_sid\x18\t \x01(\t\x12&\n\nsync_state\x18\n \x01(\x0b\x32\x12.livekit.SyncState\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8b\x01\n\x12WrappedJoinRequest\x12<\n\x0b\x63ompression\x18\x01 \x01(\x0e\x32\'.livekit.WrappedJoinRequest.Compression\x12\x14\n\x0cjoin_request\x18\x02 \x01(\x0c\"!\n\x0b\x43ompression\x12\x08\n\x04NONE\x10\x00\x12\x08\n\x04GZIP\x10\x01\"B\n\x18MediaSectionsRequirement\x12\x12\n\nnum_audios\x18\x01 \x01(\r\x12\x12\n\nnum_videos\x18\x02 \x01(\r*-\n\x0cSignalTarget\x12\r\n\tPUBLISHER\x10\x00\x12\x0e\n\nSUBSCRIBER\x10\x01*%\n\x0bStreamState\x12\n\n\x06\x41\x43TIVE\x10\x00\x12\n\n\x06PAUSED\x10\x01*.\n\x11\x43\x61ndidateProtocol\x12\x07\n\x03UDP\x10\x00\x12\x07\n\x03TCP\x10\x01\x12\x07\n\x03TLS\x10\x02\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rtc', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None + _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_SIGNALREQUEST'].fields_by_name['update_layers']._loaded_options = None + _globals['_SIGNALREQUEST'].fields_by_name['update_layers']._serialized_options = b'\030\001' + _globals['_ADDTRACKREQUEST'].fields_by_name['disable_dtx']._loaded_options = None + _globals['_ADDTRACKREQUEST'].fields_by_name['disable_dtx']._serialized_options = b'\030\001' + _globals['_ADDTRACKREQUEST'].fields_by_name['stereo']._loaded_options = None + _globals['_ADDTRACKREQUEST'].fields_by_name['stereo']._serialized_options = b'\030\001' + _globals['_DATATRACKSUBSCRIBERHANDLES_SUBHANDLESENTRY']._loaded_options = None + _globals['_DATATRACKSUBSCRIBERHANDLES_SUBHANDLESENTRY']._serialized_options = b'8\001' + _globals['_SESSIONDESCRIPTION_MIDTOTRACKIDENTRY']._loaded_options = None + _globals['_SESSIONDESCRIPTION_MIDTOTRACKIDENTRY']._serialized_options = b'8\001' + _globals['_SESSIONDESCRIPTION'].fields_by_name['mid_to_track_id']._loaded_options = None + _globals['_SESSIONDESCRIPTION'].fields_by_name['mid_to_track_id']._serialized_options = b'\272P\014midToTrackID' + _globals['_STOREDATABLOBREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_STOREDATABLOBREQUEST'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_STOREDATABLOBRESPONSE'].fields_by_name['request_id']._loaded_options = None + _globals['_STOREDATABLOBRESPONSE'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_GETDATABLOBREQUEST'].fields_by_name['request_id']._loaded_options = None + _globals['_GETDATABLOBREQUEST'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_GETDATABLOBRESPONSE'].fields_by_name['request_id']._loaded_options = None + _globals['_GETDATABLOBRESPONSE'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_UPDATEVIDEOLAYERS']._loaded_options = None + _globals['_UPDATEVIDEOLAYERS']._serialized_options = b'\030\001' + _globals['_UPDATEPARTICIPANTMETADATA_ATTRIBUTESENTRY']._loaded_options = None + _globals['_UPDATEPARTICIPANTMETADATA_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['metadata']._loaded_options = None + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['name']._loaded_options = None + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['name']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['attributes']._loaded_options = None + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['request_id']._loaded_options = None + _globals['_UPDATEPARTICIPANTMETADATA'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_ICESERVER'].fields_by_name['username']._loaded_options = None + _globals['_ICESERVER'].fields_by_name['username']._serialized_options = b'\300P\001' + _globals['_ICESERVER'].fields_by_name['credential']._loaded_options = None + _globals['_ICESERVER'].fields_by_name['credential']._serialized_options = b'\300P\002' + _globals['_SUBSCRIBEDQUALITYUPDATE'].fields_by_name['subscribed_qualities']._loaded_options = None + _globals['_SUBSCRIBEDQUALITYUPDATE'].fields_by_name['subscribed_qualities']._serialized_options = b'\030\001' + _globals['_REQUESTRESPONSE'].fields_by_name['request_id']._loaded_options = None + _globals['_REQUESTRESPONSE'].fields_by_name['request_id']._serialized_options = b'\272P\trequestID' + _globals['_JOINREQUEST_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_JOINREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_JOINREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_JOINREQUEST'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_JOINREQUEST'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_JOINREQUEST'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_SIGNALTARGET']._serialized_start=11894 + _globals['_SIGNALTARGET']._serialized_end=11939 + _globals['_STREAMSTATE']._serialized_start=11941 + _globals['_STREAMSTATE']._serialized_end=11978 + _globals['_CANDIDATEPROTOCOL']._serialized_start=11980 + _globals['_CANDIDATEPROTOCOL']._serialized_end=12026 + _globals['_SIGNALREQUEST']._serialized_start=75 + _globals['_SIGNALREQUEST']._serialized_end=1273 + _globals['_SIGNALRESPONSE']._serialized_start=1276 + _globals['_SIGNALRESPONSE']._serialized_end=2966 + _globals['_SIMULCASTCODEC']._serialized_start=2969 + _globals['_SIMULCASTCODEC']._serialized_end=3102 + _globals['_ADDTRACKREQUEST']._serialized_start=3105 + _globals['_ADDTRACKREQUEST']._serialized_end=3669 + _globals['_PUBLISHDATATRACKREQUEST']._serialized_start=3672 + _globals['_PUBLISHDATATRACKREQUEST']._serialized_end=3918 + _globals['_PUBLISHDATATRACKRESPONSE']._serialized_start=3920 + _globals['_PUBLISHDATATRACKRESPONSE']._serialized_end=3984 + _globals['_UNPUBLISHDATATRACKREQUEST']._serialized_start=3986 + _globals['_UNPUBLISHDATATRACKREQUEST']._serialized_end=4033 + _globals['_UNPUBLISHDATATRACKRESPONSE']._serialized_start=4035 + _globals['_UNPUBLISHDATATRACKRESPONSE']._serialized_end=4101 + _globals['_DATATRACKSUBSCRIBERHANDLES']._serialized_start=4104 + _globals['_DATATRACKSUBSCRIBERHANDLES']._serialized_end=4405 + _globals['_DATATRACKSUBSCRIBERHANDLES_PUBLISHEDDATATRACK']._serialized_start=4208 + _globals['_DATATRACKSUBSCRIBERHANDLES_PUBLISHEDDATATRACK']._serialized_end=4298 + _globals['_DATATRACKSUBSCRIBERHANDLES_SUBHANDLESENTRY']._serialized_start=4300 + _globals['_DATATRACKSUBSCRIBERHANDLES_SUBHANDLESENTRY']._serialized_end=4405 + _globals['_TRICKLEREQUEST']._serialized_start=4407 + _globals['_TRICKLEREQUEST']._serialized_end=4500 + _globals['_MUTETRACKREQUEST']._serialized_start=4502 + _globals['_MUTETRACKREQUEST']._serialized_end=4548 + _globals['_JOINRESPONSE']._serialized_start=4551 + _globals['_JOINRESPONSE']._serialized_end=5074 + _globals['_RECONNECTRESPONSE']._serialized_start=5077 + _globals['_RECONNECTRESPONSE']._serialized_end=5265 + _globals['_TRACKPUBLISHEDRESPONSE']._serialized_start=5267 + _globals['_TRACKPUBLISHEDRESPONSE']._serialized_end=5339 + _globals['_TRACKUNPUBLISHEDRESPONSE']._serialized_start=5341 + _globals['_TRACKUNPUBLISHEDRESPONSE']._serialized_end=5386 + _globals['_SESSIONDESCRIPTION']._serialized_start=5389 + _globals['_SESSIONDESCRIPTION']._serialized_end=5590 + _globals['_SESSIONDESCRIPTION_MIDTOTRACKIDENTRY']._serialized_start=5539 + _globals['_SESSIONDESCRIPTION_MIDTOTRACKIDENTRY']._serialized_end=5590 + _globals['_PARTICIPANTUPDATE']._serialized_start=5592 + _globals['_PARTICIPANTUPDATE']._serialized_end=5659 + _globals['_UPDATESUBSCRIPTION']._serialized_start=5661 + _globals['_UPDATESUBSCRIPTION']._serialized_end=5776 + _globals['_UPDATEDATASUBSCRIPTION']._serialized_start=5779 + _globals['_UPDATEDATASUBSCRIPTION']._serialized_end=5964 + _globals['_UPDATEDATASUBSCRIPTION_UPDATE']._serialized_start=5862 + _globals['_UPDATEDATASUBSCRIPTION_UPDATE']._serialized_end=5964 + _globals['_STOREDATABLOBREQUEST']._serialized_start=5966 + _globals['_STOREDATABLOBREQUEST']._serialized_end=6055 + _globals['_STOREDATABLOBRESPONSE']._serialized_start=6057 + _globals['_STOREDATABLOBRESPONSE']._serialized_end=6149 + _globals['_GETDATABLOBREQUEST']._serialized_start=6151 + _globals['_GETDATABLOBREQUEST']._serialized_end=6270 + _globals['_GETDATABLOBRESPONSE']._serialized_start=6272 + _globals['_GETDATABLOBRESPONSE']._serialized_end=6360 + _globals['_UPDATETRACKSETTINGS']._serialized_start=6363 + _globals['_UPDATETRACKSETTINGS']._serialized_end=6524 + _globals['_UPDATELOCALAUDIOTRACK']._serialized_start=6526 + _globals['_UPDATELOCALAUDIOTRACK']._serialized_end=6614 + _globals['_UPDATELOCALVIDEOTRACK']._serialized_start=6616 + _globals['_UPDATELOCALVIDEOTRACK']._serialized_end=6689 + _globals['_LEAVEREQUEST']._serialized_start=6692 + _globals['_LEAVEREQUEST']._serialized_end=6913 + _globals['_LEAVEREQUEST_ACTION']._serialized_start=6862 + _globals['_LEAVEREQUEST_ACTION']._serialized_end=6913 + _globals['_UPDATEVIDEOLAYERS']._serialized_start=6915 + _globals['_UPDATEVIDEOLAYERS']._serialized_end=6994 + _globals['_UPDATEPARTICIPANTMETADATA']._serialized_start=6997 + _globals['_UPDATEPARTICIPANTMETADATA']._serialized_end=7327 + _globals['_UPDATEPARTICIPANTMETADATA_ATTRIBUTESENTRY']._serialized_start=7278 + _globals['_UPDATEPARTICIPANTMETADATA_ATTRIBUTESENTRY']._serialized_end=7327 + _globals['_ICESERVER']._serialized_start=7329 + _globals['_ICESERVER']._serialized_end=7402 + _globals['_SPEAKERSCHANGED']._serialized_start=7404 + _globals['_SPEAKERSCHANGED']._serialized_end=7461 + _globals['_ROOMUPDATE']._serialized_start=7463 + _globals['_ROOMUPDATE']._serialized_end=7504 + _globals['_CONNECTIONQUALITYINFO']._serialized_start=7506 + _globals['_CONNECTIONQUALITYINFO']._serialized_end=7614 + _globals['_CONNECTIONQUALITYUPDATE']._serialized_start=7616 + _globals['_CONNECTIONQUALITYUPDATE']._serialized_end=7690 + _globals['_STREAMSTATEINFO']._serialized_start=7692 + _globals['_STREAMSTATEINFO']._serialized_end=7790 + _globals['_STREAMSTATEUPDATE']._serialized_start=7792 + _globals['_STREAMSTATEUPDATE']._serialized_end=7860 + _globals['_SUBSCRIBEDQUALITY']._serialized_start=7862 + _globals['_SUBSCRIBEDQUALITY']._serialized_end=7938 + _globals['_SUBSCRIBEDCODEC']._serialized_start=7940 + _globals['_SUBSCRIBEDCODEC']._serialized_end=8019 + _globals['_SUBSCRIBEDQUALITYUPDATE']._serialized_start=8022 + _globals['_SUBSCRIBEDQUALITYUPDATE']._serialized_end=8181 + _globals['_SUBSCRIBEDAUDIOCODECUPDATE']._serialized_start=8183 + _globals['_SUBSCRIBEDAUDIOCODECUPDATE']._serialized_end=8294 + _globals['_TRACKPERMISSION']._serialized_start=8296 + _globals['_TRACKPERMISSION']._serialized_end=8408 + _globals['_SUBSCRIPTIONPERMISSION']._serialized_start=8410 + _globals['_SUBSCRIPTIONPERMISSION']._serialized_end=8513 + _globals['_SUBSCRIPTIONPERMISSIONUPDATE']._serialized_start=8515 + _globals['_SUBSCRIPTIONPERMISSIONUPDATE']._serialized_end=8606 + _globals['_ROOMMOVEDRESPONSE']._serialized_start=8609 + _globals['_ROOMMOVEDRESPONSE']._serialized_end=8773 + _globals['_SYNCSTATE']._serialized_start=8776 + _globals['_SYNCSTATE']._serialized_end=9196 + _globals['_DATACHANNELRECEIVESTATE']._serialized_start=9198 + _globals['_DATACHANNELRECEIVESTATE']._serialized_end=9264 + _globals['_DATACHANNELINFO']._serialized_start=9266 + _globals['_DATACHANNELINFO']._serialized_end=9349 + _globals['_SIMULATESCENARIO']._serialized_start=9352 + _globals['_SIMULATESCENARIO']._serialized_end=9704 + _globals['_PING']._serialized_start=9706 + _globals['_PING']._serialized_end=9744 + _globals['_PONG']._serialized_start=9746 + _globals['_PONG']._serialized_end=9800 + _globals['_REGIONSETTINGS']._serialized_start=9802 + _globals['_REGIONSETTINGS']._serialized_end=9856 + _globals['_REGIONINFO']._serialized_start=9858 + _globals['_REGIONINFO']._serialized_end=9917 + _globals['_SUBSCRIPTIONRESPONSE']._serialized_start=9919 + _globals['_SUBSCRIPTIONRESPONSE']._serialized_end=10001 + _globals['_REQUESTRESPONSE']._serialized_start=10004 + _globals['_REQUESTRESPONSE']._serialized_end=10815 + _globals['_REQUESTRESPONSE_REASON']._serialized_start=10577 + _globals['_REQUESTRESPONSE_REASON']._serialized_end=10804 + _globals['_TRACKSUBSCRIBED']._serialized_start=10817 + _globals['_TRACKSUBSCRIBED']._serialized_end=10853 + _globals['_CONNECTIONSETTINGS']._serialized_start=10856 + _globals['_CONNECTIONSETTINGS']._serialized_end=11085 + _globals['_JOINREQUEST']._serialized_start=11088 + _globals['_JOINREQUEST']._serialized_end=11682 + _globals['_JOINREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=11622 + _globals['_JOINREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=11682 + _globals['_WRAPPEDJOINREQUEST']._serialized_start=11685 + _globals['_WRAPPEDJOINREQUEST']._serialized_end=11824 + _globals['_WRAPPEDJOINREQUEST_COMPRESSION']._serialized_start=11791 + _globals['_WRAPPEDJOINREQUEST_COMPRESSION']._serialized_end=11824 + _globals['_MEDIASECTIONSREQUIREMENT']._serialized_start=11826 + _globals['_MEDIASECTIONSREQUIREMENT']._serialized_end=11892 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/rtc.pyi b/livekit-protocol/livekit/protocol/rtc.pyi new file mode 100644 index 00000000..fb351778 --- /dev/null +++ b/livekit-protocol/livekit/protocol/rtc.pyi @@ -0,0 +1,850 @@ +from . import models as _models +from .logger_pb import options as _options_pb2 +from google.protobuf.internal import containers as _containers +from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Mapping, Optional as _Optional, Union as _Union + +DESCRIPTOR: _descriptor.FileDescriptor + +class SignalTarget(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PUBLISHER: _ClassVar[SignalTarget] + SUBSCRIBER: _ClassVar[SignalTarget] + +class StreamState(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + ACTIVE: _ClassVar[StreamState] + PAUSED: _ClassVar[StreamState] + +class CandidateProtocol(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + UDP: _ClassVar[CandidateProtocol] + TCP: _ClassVar[CandidateProtocol] + TLS: _ClassVar[CandidateProtocol] +PUBLISHER: SignalTarget +SUBSCRIBER: SignalTarget +ACTIVE: StreamState +PAUSED: StreamState +UDP: CandidateProtocol +TCP: CandidateProtocol +TLS: CandidateProtocol + +class SignalRequest(_message.Message): + __slots__ = ("offer", "answer", "trickle", "add_track", "mute", "subscription", "track_setting", "leave", "update_layers", "subscription_permission", "sync_state", "simulate", "ping", "update_metadata", "ping_req", "update_audio_track", "update_video_track", "publish_data_track_request", "unpublish_data_track_request", "update_data_subscription", "store_data_blob_request", "get_data_blob_request") + OFFER_FIELD_NUMBER: _ClassVar[int] + ANSWER_FIELD_NUMBER: _ClassVar[int] + TRICKLE_FIELD_NUMBER: _ClassVar[int] + ADD_TRACK_FIELD_NUMBER: _ClassVar[int] + MUTE_FIELD_NUMBER: _ClassVar[int] + SUBSCRIPTION_FIELD_NUMBER: _ClassVar[int] + TRACK_SETTING_FIELD_NUMBER: _ClassVar[int] + LEAVE_FIELD_NUMBER: _ClassVar[int] + UPDATE_LAYERS_FIELD_NUMBER: _ClassVar[int] + SUBSCRIPTION_PERMISSION_FIELD_NUMBER: _ClassVar[int] + SYNC_STATE_FIELD_NUMBER: _ClassVar[int] + SIMULATE_FIELD_NUMBER: _ClassVar[int] + PING_FIELD_NUMBER: _ClassVar[int] + UPDATE_METADATA_FIELD_NUMBER: _ClassVar[int] + PING_REQ_FIELD_NUMBER: _ClassVar[int] + UPDATE_AUDIO_TRACK_FIELD_NUMBER: _ClassVar[int] + UPDATE_VIDEO_TRACK_FIELD_NUMBER: _ClassVar[int] + PUBLISH_DATA_TRACK_REQUEST_FIELD_NUMBER: _ClassVar[int] + UNPUBLISH_DATA_TRACK_REQUEST_FIELD_NUMBER: _ClassVar[int] + UPDATE_DATA_SUBSCRIPTION_FIELD_NUMBER: _ClassVar[int] + STORE_DATA_BLOB_REQUEST_FIELD_NUMBER: _ClassVar[int] + GET_DATA_BLOB_REQUEST_FIELD_NUMBER: _ClassVar[int] + offer: SessionDescription + answer: SessionDescription + trickle: TrickleRequest + add_track: AddTrackRequest + mute: MuteTrackRequest + subscription: UpdateSubscription + track_setting: UpdateTrackSettings + leave: LeaveRequest + update_layers: UpdateVideoLayers + subscription_permission: SubscriptionPermission + sync_state: SyncState + simulate: SimulateScenario + ping: int + update_metadata: UpdateParticipantMetadata + ping_req: Ping + update_audio_track: UpdateLocalAudioTrack + update_video_track: UpdateLocalVideoTrack + publish_data_track_request: PublishDataTrackRequest + unpublish_data_track_request: UnpublishDataTrackRequest + update_data_subscription: UpdateDataSubscription + store_data_blob_request: StoreDataBlobRequest + get_data_blob_request: GetDataBlobRequest + def __init__(self, offer: _Optional[_Union[SessionDescription, _Mapping]] = ..., answer: _Optional[_Union[SessionDescription, _Mapping]] = ..., trickle: _Optional[_Union[TrickleRequest, _Mapping]] = ..., add_track: _Optional[_Union[AddTrackRequest, _Mapping]] = ..., mute: _Optional[_Union[MuteTrackRequest, _Mapping]] = ..., subscription: _Optional[_Union[UpdateSubscription, _Mapping]] = ..., track_setting: _Optional[_Union[UpdateTrackSettings, _Mapping]] = ..., leave: _Optional[_Union[LeaveRequest, _Mapping]] = ..., update_layers: _Optional[_Union[UpdateVideoLayers, _Mapping]] = ..., subscription_permission: _Optional[_Union[SubscriptionPermission, _Mapping]] = ..., sync_state: _Optional[_Union[SyncState, _Mapping]] = ..., simulate: _Optional[_Union[SimulateScenario, _Mapping]] = ..., ping: _Optional[int] = ..., update_metadata: _Optional[_Union[UpdateParticipantMetadata, _Mapping]] = ..., ping_req: _Optional[_Union[Ping, _Mapping]] = ..., update_audio_track: _Optional[_Union[UpdateLocalAudioTrack, _Mapping]] = ..., update_video_track: _Optional[_Union[UpdateLocalVideoTrack, _Mapping]] = ..., publish_data_track_request: _Optional[_Union[PublishDataTrackRequest, _Mapping]] = ..., unpublish_data_track_request: _Optional[_Union[UnpublishDataTrackRequest, _Mapping]] = ..., update_data_subscription: _Optional[_Union[UpdateDataSubscription, _Mapping]] = ..., store_data_blob_request: _Optional[_Union[StoreDataBlobRequest, _Mapping]] = ..., get_data_blob_request: _Optional[_Union[GetDataBlobRequest, _Mapping]] = ...) -> None: ... + +class SignalResponse(_message.Message): + __slots__ = ("join", "answer", "offer", "trickle", "update", "track_published", "leave", "mute", "speakers_changed", "room_update", "connection_quality", "stream_state_update", "subscribed_quality_update", "subscription_permission_update", "refresh_token", "track_unpublished", "pong", "reconnect", "pong_resp", "subscription_response", "request_response", "track_subscribed", "room_moved", "media_sections_requirement", "subscribed_audio_codec_update", "publish_data_track_response", "unpublish_data_track_response", "data_track_subscriber_handles", "store_data_blob_response", "get_data_blob_response") + JOIN_FIELD_NUMBER: _ClassVar[int] + ANSWER_FIELD_NUMBER: _ClassVar[int] + OFFER_FIELD_NUMBER: _ClassVar[int] + TRICKLE_FIELD_NUMBER: _ClassVar[int] + UPDATE_FIELD_NUMBER: _ClassVar[int] + TRACK_PUBLISHED_FIELD_NUMBER: _ClassVar[int] + LEAVE_FIELD_NUMBER: _ClassVar[int] + MUTE_FIELD_NUMBER: _ClassVar[int] + SPEAKERS_CHANGED_FIELD_NUMBER: _ClassVar[int] + ROOM_UPDATE_FIELD_NUMBER: _ClassVar[int] + CONNECTION_QUALITY_FIELD_NUMBER: _ClassVar[int] + STREAM_STATE_UPDATE_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBED_QUALITY_UPDATE_FIELD_NUMBER: _ClassVar[int] + SUBSCRIPTION_PERMISSION_UPDATE_FIELD_NUMBER: _ClassVar[int] + REFRESH_TOKEN_FIELD_NUMBER: _ClassVar[int] + TRACK_UNPUBLISHED_FIELD_NUMBER: _ClassVar[int] + PONG_FIELD_NUMBER: _ClassVar[int] + RECONNECT_FIELD_NUMBER: _ClassVar[int] + PONG_RESP_FIELD_NUMBER: _ClassVar[int] + SUBSCRIPTION_RESPONSE_FIELD_NUMBER: _ClassVar[int] + REQUEST_RESPONSE_FIELD_NUMBER: _ClassVar[int] + TRACK_SUBSCRIBED_FIELD_NUMBER: _ClassVar[int] + ROOM_MOVED_FIELD_NUMBER: _ClassVar[int] + MEDIA_SECTIONS_REQUIREMENT_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBED_AUDIO_CODEC_UPDATE_FIELD_NUMBER: _ClassVar[int] + PUBLISH_DATA_TRACK_RESPONSE_FIELD_NUMBER: _ClassVar[int] + UNPUBLISH_DATA_TRACK_RESPONSE_FIELD_NUMBER: _ClassVar[int] + DATA_TRACK_SUBSCRIBER_HANDLES_FIELD_NUMBER: _ClassVar[int] + STORE_DATA_BLOB_RESPONSE_FIELD_NUMBER: _ClassVar[int] + GET_DATA_BLOB_RESPONSE_FIELD_NUMBER: _ClassVar[int] + join: JoinResponse + answer: SessionDescription + offer: SessionDescription + trickle: TrickleRequest + update: ParticipantUpdate + track_published: TrackPublishedResponse + leave: LeaveRequest + mute: MuteTrackRequest + speakers_changed: SpeakersChanged + room_update: RoomUpdate + connection_quality: ConnectionQualityUpdate + stream_state_update: StreamStateUpdate + subscribed_quality_update: SubscribedQualityUpdate + subscription_permission_update: SubscriptionPermissionUpdate + refresh_token: str + track_unpublished: TrackUnpublishedResponse + pong: int + reconnect: ReconnectResponse + pong_resp: Pong + subscription_response: SubscriptionResponse + request_response: RequestResponse + track_subscribed: TrackSubscribed + room_moved: RoomMovedResponse + media_sections_requirement: MediaSectionsRequirement + subscribed_audio_codec_update: SubscribedAudioCodecUpdate + publish_data_track_response: PublishDataTrackResponse + unpublish_data_track_response: UnpublishDataTrackResponse + data_track_subscriber_handles: DataTrackSubscriberHandles + store_data_blob_response: StoreDataBlobResponse + get_data_blob_response: GetDataBlobResponse + def __init__(self, join: _Optional[_Union[JoinResponse, _Mapping]] = ..., answer: _Optional[_Union[SessionDescription, _Mapping]] = ..., offer: _Optional[_Union[SessionDescription, _Mapping]] = ..., trickle: _Optional[_Union[TrickleRequest, _Mapping]] = ..., update: _Optional[_Union[ParticipantUpdate, _Mapping]] = ..., track_published: _Optional[_Union[TrackPublishedResponse, _Mapping]] = ..., leave: _Optional[_Union[LeaveRequest, _Mapping]] = ..., mute: _Optional[_Union[MuteTrackRequest, _Mapping]] = ..., speakers_changed: _Optional[_Union[SpeakersChanged, _Mapping]] = ..., room_update: _Optional[_Union[RoomUpdate, _Mapping]] = ..., connection_quality: _Optional[_Union[ConnectionQualityUpdate, _Mapping]] = ..., stream_state_update: _Optional[_Union[StreamStateUpdate, _Mapping]] = ..., subscribed_quality_update: _Optional[_Union[SubscribedQualityUpdate, _Mapping]] = ..., subscription_permission_update: _Optional[_Union[SubscriptionPermissionUpdate, _Mapping]] = ..., refresh_token: _Optional[str] = ..., track_unpublished: _Optional[_Union[TrackUnpublishedResponse, _Mapping]] = ..., pong: _Optional[int] = ..., reconnect: _Optional[_Union[ReconnectResponse, _Mapping]] = ..., pong_resp: _Optional[_Union[Pong, _Mapping]] = ..., subscription_response: _Optional[_Union[SubscriptionResponse, _Mapping]] = ..., request_response: _Optional[_Union[RequestResponse, _Mapping]] = ..., track_subscribed: _Optional[_Union[TrackSubscribed, _Mapping]] = ..., room_moved: _Optional[_Union[RoomMovedResponse, _Mapping]] = ..., media_sections_requirement: _Optional[_Union[MediaSectionsRequirement, _Mapping]] = ..., subscribed_audio_codec_update: _Optional[_Union[SubscribedAudioCodecUpdate, _Mapping]] = ..., publish_data_track_response: _Optional[_Union[PublishDataTrackResponse, _Mapping]] = ..., unpublish_data_track_response: _Optional[_Union[UnpublishDataTrackResponse, _Mapping]] = ..., data_track_subscriber_handles: _Optional[_Union[DataTrackSubscriberHandles, _Mapping]] = ..., store_data_blob_response: _Optional[_Union[StoreDataBlobResponse, _Mapping]] = ..., get_data_blob_response: _Optional[_Union[GetDataBlobResponse, _Mapping]] = ...) -> None: ... + +class SimulcastCodec(_message.Message): + __slots__ = ("codec", "cid", "layers", "video_layer_mode") + CODEC_FIELD_NUMBER: _ClassVar[int] + CID_FIELD_NUMBER: _ClassVar[int] + LAYERS_FIELD_NUMBER: _ClassVar[int] + VIDEO_LAYER_MODE_FIELD_NUMBER: _ClassVar[int] + codec: str + cid: str + layers: _containers.RepeatedCompositeFieldContainer[_models.VideoLayer] + video_layer_mode: _models.VideoLayer.Mode + def __init__(self, codec: _Optional[str] = ..., cid: _Optional[str] = ..., layers: _Optional[_Iterable[_Union[_models.VideoLayer, _Mapping]]] = ..., video_layer_mode: _Optional[_Union[_models.VideoLayer.Mode, str]] = ...) -> None: ... + +class AddTrackRequest(_message.Message): + __slots__ = ("cid", "name", "type", "width", "height", "muted", "disable_dtx", "source", "layers", "simulcast_codecs", "sid", "stereo", "disable_red", "encryption", "stream", "backup_codec_policy", "audio_features", "packet_trailer_features") + CID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + MUTED_FIELD_NUMBER: _ClassVar[int] + DISABLE_DTX_FIELD_NUMBER: _ClassVar[int] + SOURCE_FIELD_NUMBER: _ClassVar[int] + LAYERS_FIELD_NUMBER: _ClassVar[int] + SIMULCAST_CODECS_FIELD_NUMBER: _ClassVar[int] + SID_FIELD_NUMBER: _ClassVar[int] + STEREO_FIELD_NUMBER: _ClassVar[int] + DISABLE_RED_FIELD_NUMBER: _ClassVar[int] + ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + STREAM_FIELD_NUMBER: _ClassVar[int] + BACKUP_CODEC_POLICY_FIELD_NUMBER: _ClassVar[int] + AUDIO_FEATURES_FIELD_NUMBER: _ClassVar[int] + PACKET_TRAILER_FEATURES_FIELD_NUMBER: _ClassVar[int] + cid: str + name: str + type: _models.TrackType + width: int + height: int + muted: bool + disable_dtx: bool + source: _models.TrackSource + layers: _containers.RepeatedCompositeFieldContainer[_models.VideoLayer] + simulcast_codecs: _containers.RepeatedCompositeFieldContainer[SimulcastCodec] + sid: str + stereo: bool + disable_red: bool + encryption: _models.Encryption.Type + stream: str + backup_codec_policy: _models.BackupCodecPolicy + audio_features: _containers.RepeatedScalarFieldContainer[_models.AudioTrackFeature] + packet_trailer_features: _containers.RepeatedScalarFieldContainer[_models.PacketTrailerFeature] + def __init__(self, cid: _Optional[str] = ..., name: _Optional[str] = ..., type: _Optional[_Union[_models.TrackType, str]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., muted: bool = ..., disable_dtx: bool = ..., source: _Optional[_Union[_models.TrackSource, str]] = ..., layers: _Optional[_Iterable[_Union[_models.VideoLayer, _Mapping]]] = ..., simulcast_codecs: _Optional[_Iterable[_Union[SimulcastCodec, _Mapping]]] = ..., sid: _Optional[str] = ..., stereo: bool = ..., disable_red: bool = ..., encryption: _Optional[_Union[_models.Encryption.Type, str]] = ..., stream: _Optional[str] = ..., backup_codec_policy: _Optional[_Union[_models.BackupCodecPolicy, str]] = ..., audio_features: _Optional[_Iterable[_Union[_models.AudioTrackFeature, str]]] = ..., packet_trailer_features: _Optional[_Iterable[_Union[_models.PacketTrailerFeature, str]]] = ...) -> None: ... + +class PublishDataTrackRequest(_message.Message): + __slots__ = ("pub_handle", "name", "encryption", "frame_encoding", "schema") + PUB_HANDLE_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + FRAME_ENCODING_FIELD_NUMBER: _ClassVar[int] + SCHEMA_FIELD_NUMBER: _ClassVar[int] + pub_handle: int + name: str + encryption: _models.Encryption.Type + frame_encoding: _models.DataTrackFrameEncoding + schema: _models.DataTrackSchemaId + def __init__(self, pub_handle: _Optional[int] = ..., name: _Optional[str] = ..., encryption: _Optional[_Union[_models.Encryption.Type, str]] = ..., frame_encoding: _Optional[_Union[_models.DataTrackFrameEncoding, _Mapping]] = ..., schema: _Optional[_Union[_models.DataTrackSchemaId, _Mapping]] = ...) -> None: ... + +class PublishDataTrackResponse(_message.Message): + __slots__ = ("info",) + INFO_FIELD_NUMBER: _ClassVar[int] + info: _models.DataTrackInfo + def __init__(self, info: _Optional[_Union[_models.DataTrackInfo, _Mapping]] = ...) -> None: ... + +class UnpublishDataTrackRequest(_message.Message): + __slots__ = ("pub_handle",) + PUB_HANDLE_FIELD_NUMBER: _ClassVar[int] + pub_handle: int + def __init__(self, pub_handle: _Optional[int] = ...) -> None: ... + +class UnpublishDataTrackResponse(_message.Message): + __slots__ = ("info",) + INFO_FIELD_NUMBER: _ClassVar[int] + info: _models.DataTrackInfo + def __init__(self, info: _Optional[_Union[_models.DataTrackInfo, _Mapping]] = ...) -> None: ... + +class DataTrackSubscriberHandles(_message.Message): + __slots__ = ("sub_handles",) + class PublishedDataTrack(_message.Message): + __slots__ = ("publisher_identity", "publisher_sid", "track_sid") + PUBLISHER_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PUBLISHER_SID_FIELD_NUMBER: _ClassVar[int] + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + publisher_identity: str + publisher_sid: str + track_sid: str + def __init__(self, publisher_identity: _Optional[str] = ..., publisher_sid: _Optional[str] = ..., track_sid: _Optional[str] = ...) -> None: ... + class SubHandlesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: int + value: DataTrackSubscriberHandles.PublishedDataTrack + def __init__(self, key: _Optional[int] = ..., value: _Optional[_Union[DataTrackSubscriberHandles.PublishedDataTrack, _Mapping]] = ...) -> None: ... + SUB_HANDLES_FIELD_NUMBER: _ClassVar[int] + sub_handles: _containers.MessageMap[int, DataTrackSubscriberHandles.PublishedDataTrack] + def __init__(self, sub_handles: _Optional[_Mapping[int, DataTrackSubscriberHandles.PublishedDataTrack]] = ...) -> None: ... + +class TrickleRequest(_message.Message): + __slots__ = ("candidateInit", "target", "final") + CANDIDATEINIT_FIELD_NUMBER: _ClassVar[int] + TARGET_FIELD_NUMBER: _ClassVar[int] + FINAL_FIELD_NUMBER: _ClassVar[int] + candidateInit: str + target: SignalTarget + final: bool + def __init__(self, candidateInit: _Optional[str] = ..., target: _Optional[_Union[SignalTarget, str]] = ..., final: bool = ...) -> None: ... + +class MuteTrackRequest(_message.Message): + __slots__ = ("sid", "muted") + SID_FIELD_NUMBER: _ClassVar[int] + MUTED_FIELD_NUMBER: _ClassVar[int] + sid: str + muted: bool + def __init__(self, sid: _Optional[str] = ..., muted: bool = ...) -> None: ... + +class JoinResponse(_message.Message): + __slots__ = ("room", "participant", "other_participants", "server_version", "ice_servers", "subscriber_primary", "alternative_url", "client_configuration", "server_region", "ping_timeout", "ping_interval", "server_info", "sif_trailer", "enabled_publish_codecs", "fast_publish") + ROOM_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_FIELD_NUMBER: _ClassVar[int] + OTHER_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] + SERVER_VERSION_FIELD_NUMBER: _ClassVar[int] + ICE_SERVERS_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBER_PRIMARY_FIELD_NUMBER: _ClassVar[int] + ALTERNATIVE_URL_FIELD_NUMBER: _ClassVar[int] + CLIENT_CONFIGURATION_FIELD_NUMBER: _ClassVar[int] + SERVER_REGION_FIELD_NUMBER: _ClassVar[int] + PING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + PING_INTERVAL_FIELD_NUMBER: _ClassVar[int] + SERVER_INFO_FIELD_NUMBER: _ClassVar[int] + SIF_TRAILER_FIELD_NUMBER: _ClassVar[int] + ENABLED_PUBLISH_CODECS_FIELD_NUMBER: _ClassVar[int] + FAST_PUBLISH_FIELD_NUMBER: _ClassVar[int] + room: _models.Room + participant: _models.ParticipantInfo + other_participants: _containers.RepeatedCompositeFieldContainer[_models.ParticipantInfo] + server_version: str + ice_servers: _containers.RepeatedCompositeFieldContainer[ICEServer] + subscriber_primary: bool + alternative_url: str + client_configuration: _models.ClientConfiguration + server_region: str + ping_timeout: int + ping_interval: int + server_info: _models.ServerInfo + sif_trailer: bytes + enabled_publish_codecs: _containers.RepeatedCompositeFieldContainer[_models.Codec] + fast_publish: bool + def __init__(self, room: _Optional[_Union[_models.Room, _Mapping]] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., other_participants: _Optional[_Iterable[_Union[_models.ParticipantInfo, _Mapping]]] = ..., server_version: _Optional[str] = ..., ice_servers: _Optional[_Iterable[_Union[ICEServer, _Mapping]]] = ..., subscriber_primary: bool = ..., alternative_url: _Optional[str] = ..., client_configuration: _Optional[_Union[_models.ClientConfiguration, _Mapping]] = ..., server_region: _Optional[str] = ..., ping_timeout: _Optional[int] = ..., ping_interval: _Optional[int] = ..., server_info: _Optional[_Union[_models.ServerInfo, _Mapping]] = ..., sif_trailer: _Optional[bytes] = ..., enabled_publish_codecs: _Optional[_Iterable[_Union[_models.Codec, _Mapping]]] = ..., fast_publish: bool = ...) -> None: ... + +class ReconnectResponse(_message.Message): + __slots__ = ("ice_servers", "client_configuration", "server_info", "last_message_seq") + ICE_SERVERS_FIELD_NUMBER: _ClassVar[int] + CLIENT_CONFIGURATION_FIELD_NUMBER: _ClassVar[int] + SERVER_INFO_FIELD_NUMBER: _ClassVar[int] + LAST_MESSAGE_SEQ_FIELD_NUMBER: _ClassVar[int] + ice_servers: _containers.RepeatedCompositeFieldContainer[ICEServer] + client_configuration: _models.ClientConfiguration + server_info: _models.ServerInfo + last_message_seq: int + def __init__(self, ice_servers: _Optional[_Iterable[_Union[ICEServer, _Mapping]]] = ..., client_configuration: _Optional[_Union[_models.ClientConfiguration, _Mapping]] = ..., server_info: _Optional[_Union[_models.ServerInfo, _Mapping]] = ..., last_message_seq: _Optional[int] = ...) -> None: ... + +class TrackPublishedResponse(_message.Message): + __slots__ = ("cid", "track") + CID_FIELD_NUMBER: _ClassVar[int] + TRACK_FIELD_NUMBER: _ClassVar[int] + cid: str + track: _models.TrackInfo + def __init__(self, cid: _Optional[str] = ..., track: _Optional[_Union[_models.TrackInfo, _Mapping]] = ...) -> None: ... + +class TrackUnpublishedResponse(_message.Message): + __slots__ = ("track_sid",) + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + track_sid: str + def __init__(self, track_sid: _Optional[str] = ...) -> None: ... + +class SessionDescription(_message.Message): + __slots__ = ("type", "sdp", "id", "mid_to_track_id") + class MidToTrackIdEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + TYPE_FIELD_NUMBER: _ClassVar[int] + SDP_FIELD_NUMBER: _ClassVar[int] + ID_FIELD_NUMBER: _ClassVar[int] + MID_TO_TRACK_ID_FIELD_NUMBER: _ClassVar[int] + type: str + sdp: str + id: int + mid_to_track_id: _containers.ScalarMap[str, str] + def __init__(self, type: _Optional[str] = ..., sdp: _Optional[str] = ..., id: _Optional[int] = ..., mid_to_track_id: _Optional[_Mapping[str, str]] = ...) -> None: ... + +class ParticipantUpdate(_message.Message): + __slots__ = ("participants",) + PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] + participants: _containers.RepeatedCompositeFieldContainer[_models.ParticipantInfo] + def __init__(self, participants: _Optional[_Iterable[_Union[_models.ParticipantInfo, _Mapping]]] = ...) -> None: ... + +class UpdateSubscription(_message.Message): + __slots__ = ("track_sids", "subscribe", "participant_tracks") + TRACK_SIDS_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBE_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_TRACKS_FIELD_NUMBER: _ClassVar[int] + track_sids: _containers.RepeatedScalarFieldContainer[str] + subscribe: bool + participant_tracks: _containers.RepeatedCompositeFieldContainer[_models.ParticipantTracks] + def __init__(self, track_sids: _Optional[_Iterable[str]] = ..., subscribe: bool = ..., participant_tracks: _Optional[_Iterable[_Union[_models.ParticipantTracks, _Mapping]]] = ...) -> None: ... + +class UpdateDataSubscription(_message.Message): + __slots__ = ("updates",) + class Update(_message.Message): + __slots__ = ("track_sid", "subscribe", "options") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBE_FIELD_NUMBER: _ClassVar[int] + OPTIONS_FIELD_NUMBER: _ClassVar[int] + track_sid: str + subscribe: bool + options: _models.DataTrackSubscriptionOptions + def __init__(self, track_sid: _Optional[str] = ..., subscribe: bool = ..., options: _Optional[_Union[_models.DataTrackSubscriptionOptions, _Mapping]] = ...) -> None: ... + UPDATES_FIELD_NUMBER: _ClassVar[int] + updates: _containers.RepeatedCompositeFieldContainer[UpdateDataSubscription.Update] + def __init__(self, updates: _Optional[_Iterable[_Union[UpdateDataSubscription.Update, _Mapping]]] = ...) -> None: ... + +class StoreDataBlobRequest(_message.Message): + __slots__ = ("request_id", "blob") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + BLOB_FIELD_NUMBER: _ClassVar[int] + request_id: int + blob: _models.DataBlob + def __init__(self, request_id: _Optional[int] = ..., blob: _Optional[_Union[_models.DataBlob, _Mapping]] = ...) -> None: ... + +class StoreDataBlobResponse(_message.Message): + __slots__ = ("request_id", "key") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + KEY_FIELD_NUMBER: _ClassVar[int] + request_id: int + key: _models.DataBlobKey + def __init__(self, request_id: _Optional[int] = ..., key: _Optional[_Union[_models.DataBlobKey, _Mapping]] = ...) -> None: ... + +class GetDataBlobRequest(_message.Message): + __slots__ = ("request_id", "participant_identity", "key") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + KEY_FIELD_NUMBER: _ClassVar[int] + request_id: int + participant_identity: str + key: _models.DataBlobKey + def __init__(self, request_id: _Optional[int] = ..., participant_identity: _Optional[str] = ..., key: _Optional[_Union[_models.DataBlobKey, _Mapping]] = ...) -> None: ... + +class GetDataBlobResponse(_message.Message): + __slots__ = ("request_id", "blob") + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + BLOB_FIELD_NUMBER: _ClassVar[int] + request_id: int + blob: _models.DataBlob + def __init__(self, request_id: _Optional[int] = ..., blob: _Optional[_Union[_models.DataBlob, _Mapping]] = ...) -> None: ... + +class UpdateTrackSettings(_message.Message): + __slots__ = ("track_sids", "disabled", "quality", "width", "height", "fps", "priority") + TRACK_SIDS_FIELD_NUMBER: _ClassVar[int] + DISABLED_FIELD_NUMBER: _ClassVar[int] + QUALITY_FIELD_NUMBER: _ClassVar[int] + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + FPS_FIELD_NUMBER: _ClassVar[int] + PRIORITY_FIELD_NUMBER: _ClassVar[int] + track_sids: _containers.RepeatedScalarFieldContainer[str] + disabled: bool + quality: _models.VideoQuality + width: int + height: int + fps: int + priority: int + def __init__(self, track_sids: _Optional[_Iterable[str]] = ..., disabled: bool = ..., quality: _Optional[_Union[_models.VideoQuality, str]] = ..., width: _Optional[int] = ..., height: _Optional[int] = ..., fps: _Optional[int] = ..., priority: _Optional[int] = ...) -> None: ... + +class UpdateLocalAudioTrack(_message.Message): + __slots__ = ("track_sid", "features") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + FEATURES_FIELD_NUMBER: _ClassVar[int] + track_sid: str + features: _containers.RepeatedScalarFieldContainer[_models.AudioTrackFeature] + def __init__(self, track_sid: _Optional[str] = ..., features: _Optional[_Iterable[_Union[_models.AudioTrackFeature, str]]] = ...) -> None: ... + +class UpdateLocalVideoTrack(_message.Message): + __slots__ = ("track_sid", "width", "height") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + WIDTH_FIELD_NUMBER: _ClassVar[int] + HEIGHT_FIELD_NUMBER: _ClassVar[int] + track_sid: str + width: int + height: int + def __init__(self, track_sid: _Optional[str] = ..., width: _Optional[int] = ..., height: _Optional[int] = ...) -> None: ... + +class LeaveRequest(_message.Message): + __slots__ = ("can_reconnect", "reason", "action", "regions") + class Action(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + DISCONNECT: _ClassVar[LeaveRequest.Action] + RESUME: _ClassVar[LeaveRequest.Action] + RECONNECT: _ClassVar[LeaveRequest.Action] + DISCONNECT: LeaveRequest.Action + RESUME: LeaveRequest.Action + RECONNECT: LeaveRequest.Action + CAN_RECONNECT_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + ACTION_FIELD_NUMBER: _ClassVar[int] + REGIONS_FIELD_NUMBER: _ClassVar[int] + can_reconnect: bool + reason: _models.DisconnectReason + action: LeaveRequest.Action + regions: RegionSettings + def __init__(self, can_reconnect: bool = ..., reason: _Optional[_Union[_models.DisconnectReason, str]] = ..., action: _Optional[_Union[LeaveRequest.Action, str]] = ..., regions: _Optional[_Union[RegionSettings, _Mapping]] = ...) -> None: ... + +class UpdateVideoLayers(_message.Message): + __slots__ = ("track_sid", "layers") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + LAYERS_FIELD_NUMBER: _ClassVar[int] + track_sid: str + layers: _containers.RepeatedCompositeFieldContainer[_models.VideoLayer] + def __init__(self, track_sid: _Optional[str] = ..., layers: _Optional[_Iterable[_Union[_models.VideoLayer, _Mapping]]] = ...) -> None: ... + +class UpdateParticipantMetadata(_message.Message): + __slots__ = ("metadata", "name", "attributes", "request_id") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + METADATA_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + metadata: str + name: str + attributes: _containers.ScalarMap[str, str] + request_id: int + def __init__(self, metadata: _Optional[str] = ..., name: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ..., request_id: _Optional[int] = ...) -> None: ... + +class ICEServer(_message.Message): + __slots__ = ("urls", "username", "credential") + URLS_FIELD_NUMBER: _ClassVar[int] + USERNAME_FIELD_NUMBER: _ClassVar[int] + CREDENTIAL_FIELD_NUMBER: _ClassVar[int] + urls: _containers.RepeatedScalarFieldContainer[str] + username: str + credential: str + def __init__(self, urls: _Optional[_Iterable[str]] = ..., username: _Optional[str] = ..., credential: _Optional[str] = ...) -> None: ... + +class SpeakersChanged(_message.Message): + __slots__ = ("speakers",) + SPEAKERS_FIELD_NUMBER: _ClassVar[int] + speakers: _containers.RepeatedCompositeFieldContainer[_models.SpeakerInfo] + def __init__(self, speakers: _Optional[_Iterable[_Union[_models.SpeakerInfo, _Mapping]]] = ...) -> None: ... + +class RoomUpdate(_message.Message): + __slots__ = ("room",) + ROOM_FIELD_NUMBER: _ClassVar[int] + room: _models.Room + def __init__(self, room: _Optional[_Union[_models.Room, _Mapping]] = ...) -> None: ... + +class ConnectionQualityInfo(_message.Message): + __slots__ = ("participant_sid", "quality", "score") + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + QUALITY_FIELD_NUMBER: _ClassVar[int] + SCORE_FIELD_NUMBER: _ClassVar[int] + participant_sid: str + quality: _models.ConnectionQuality + score: float + def __init__(self, participant_sid: _Optional[str] = ..., quality: _Optional[_Union[_models.ConnectionQuality, str]] = ..., score: _Optional[float] = ...) -> None: ... + +class ConnectionQualityUpdate(_message.Message): + __slots__ = ("updates",) + UPDATES_FIELD_NUMBER: _ClassVar[int] + updates: _containers.RepeatedCompositeFieldContainer[ConnectionQualityInfo] + def __init__(self, updates: _Optional[_Iterable[_Union[ConnectionQualityInfo, _Mapping]]] = ...) -> None: ... + +class StreamStateInfo(_message.Message): + __slots__ = ("participant_sid", "track_sid", "state") + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + STATE_FIELD_NUMBER: _ClassVar[int] + participant_sid: str + track_sid: str + state: StreamState + def __init__(self, participant_sid: _Optional[str] = ..., track_sid: _Optional[str] = ..., state: _Optional[_Union[StreamState, str]] = ...) -> None: ... + +class StreamStateUpdate(_message.Message): + __slots__ = ("stream_states",) + STREAM_STATES_FIELD_NUMBER: _ClassVar[int] + stream_states: _containers.RepeatedCompositeFieldContainer[StreamStateInfo] + def __init__(self, stream_states: _Optional[_Iterable[_Union[StreamStateInfo, _Mapping]]] = ...) -> None: ... + +class SubscribedQuality(_message.Message): + __slots__ = ("quality", "enabled") + QUALITY_FIELD_NUMBER: _ClassVar[int] + ENABLED_FIELD_NUMBER: _ClassVar[int] + quality: _models.VideoQuality + enabled: bool + def __init__(self, quality: _Optional[_Union[_models.VideoQuality, str]] = ..., enabled: bool = ...) -> None: ... + +class SubscribedCodec(_message.Message): + __slots__ = ("codec", "qualities") + CODEC_FIELD_NUMBER: _ClassVar[int] + QUALITIES_FIELD_NUMBER: _ClassVar[int] + codec: str + qualities: _containers.RepeatedCompositeFieldContainer[SubscribedQuality] + def __init__(self, codec: _Optional[str] = ..., qualities: _Optional[_Iterable[_Union[SubscribedQuality, _Mapping]]] = ...) -> None: ... + +class SubscribedQualityUpdate(_message.Message): + __slots__ = ("track_sid", "subscribed_qualities", "subscribed_codecs") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBED_QUALITIES_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBED_CODECS_FIELD_NUMBER: _ClassVar[int] + track_sid: str + subscribed_qualities: _containers.RepeatedCompositeFieldContainer[SubscribedQuality] + subscribed_codecs: _containers.RepeatedCompositeFieldContainer[SubscribedCodec] + def __init__(self, track_sid: _Optional[str] = ..., subscribed_qualities: _Optional[_Iterable[_Union[SubscribedQuality, _Mapping]]] = ..., subscribed_codecs: _Optional[_Iterable[_Union[SubscribedCodec, _Mapping]]] = ...) -> None: ... + +class SubscribedAudioCodecUpdate(_message.Message): + __slots__ = ("track_sid", "subscribed_audio_codecs") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBED_AUDIO_CODECS_FIELD_NUMBER: _ClassVar[int] + track_sid: str + subscribed_audio_codecs: _containers.RepeatedCompositeFieldContainer[_models.SubscribedAudioCodec] + def __init__(self, track_sid: _Optional[str] = ..., subscribed_audio_codecs: _Optional[_Iterable[_Union[_models.SubscribedAudioCodec, _Mapping]]] = ...) -> None: ... + +class TrackPermission(_message.Message): + __slots__ = ("participant_sid", "all_tracks", "track_sids", "participant_identity") + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + ALL_TRACKS_FIELD_NUMBER: _ClassVar[int] + TRACK_SIDS_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + participant_sid: str + all_tracks: bool + track_sids: _containers.RepeatedScalarFieldContainer[str] + participant_identity: str + def __init__(self, participant_sid: _Optional[str] = ..., all_tracks: bool = ..., track_sids: _Optional[_Iterable[str]] = ..., participant_identity: _Optional[str] = ...) -> None: ... + +class SubscriptionPermission(_message.Message): + __slots__ = ("all_participants", "track_permissions") + ALL_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] + TRACK_PERMISSIONS_FIELD_NUMBER: _ClassVar[int] + all_participants: bool + track_permissions: _containers.RepeatedCompositeFieldContainer[TrackPermission] + def __init__(self, all_participants: bool = ..., track_permissions: _Optional[_Iterable[_Union[TrackPermission, _Mapping]]] = ...) -> None: ... + +class SubscriptionPermissionUpdate(_message.Message): + __slots__ = ("participant_sid", "track_sid", "allowed") + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + ALLOWED_FIELD_NUMBER: _ClassVar[int] + participant_sid: str + track_sid: str + allowed: bool + def __init__(self, participant_sid: _Optional[str] = ..., track_sid: _Optional[str] = ..., allowed: bool = ...) -> None: ... + +class RoomMovedResponse(_message.Message): + __slots__ = ("room", "token", "participant", "other_participants") + ROOM_FIELD_NUMBER: _ClassVar[int] + TOKEN_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_FIELD_NUMBER: _ClassVar[int] + OTHER_PARTICIPANTS_FIELD_NUMBER: _ClassVar[int] + room: _models.Room + token: str + participant: _models.ParticipantInfo + other_participants: _containers.RepeatedCompositeFieldContainer[_models.ParticipantInfo] + def __init__(self, room: _Optional[_Union[_models.Room, _Mapping]] = ..., token: _Optional[str] = ..., participant: _Optional[_Union[_models.ParticipantInfo, _Mapping]] = ..., other_participants: _Optional[_Iterable[_Union[_models.ParticipantInfo, _Mapping]]] = ...) -> None: ... + +class SyncState(_message.Message): + __slots__ = ("answer", "subscription", "publish_tracks", "data_channels", "offer", "track_sids_disabled", "datachannel_receive_states", "publish_data_tracks") + ANSWER_FIELD_NUMBER: _ClassVar[int] + SUBSCRIPTION_FIELD_NUMBER: _ClassVar[int] + PUBLISH_TRACKS_FIELD_NUMBER: _ClassVar[int] + DATA_CHANNELS_FIELD_NUMBER: _ClassVar[int] + OFFER_FIELD_NUMBER: _ClassVar[int] + TRACK_SIDS_DISABLED_FIELD_NUMBER: _ClassVar[int] + DATACHANNEL_RECEIVE_STATES_FIELD_NUMBER: _ClassVar[int] + PUBLISH_DATA_TRACKS_FIELD_NUMBER: _ClassVar[int] + answer: SessionDescription + subscription: UpdateSubscription + publish_tracks: _containers.RepeatedCompositeFieldContainer[TrackPublishedResponse] + data_channels: _containers.RepeatedCompositeFieldContainer[DataChannelInfo] + offer: SessionDescription + track_sids_disabled: _containers.RepeatedScalarFieldContainer[str] + datachannel_receive_states: _containers.RepeatedCompositeFieldContainer[DataChannelReceiveState] + publish_data_tracks: _containers.RepeatedCompositeFieldContainer[PublishDataTrackResponse] + def __init__(self, answer: _Optional[_Union[SessionDescription, _Mapping]] = ..., subscription: _Optional[_Union[UpdateSubscription, _Mapping]] = ..., publish_tracks: _Optional[_Iterable[_Union[TrackPublishedResponse, _Mapping]]] = ..., data_channels: _Optional[_Iterable[_Union[DataChannelInfo, _Mapping]]] = ..., offer: _Optional[_Union[SessionDescription, _Mapping]] = ..., track_sids_disabled: _Optional[_Iterable[str]] = ..., datachannel_receive_states: _Optional[_Iterable[_Union[DataChannelReceiveState, _Mapping]]] = ..., publish_data_tracks: _Optional[_Iterable[_Union[PublishDataTrackResponse, _Mapping]]] = ...) -> None: ... + +class DataChannelReceiveState(_message.Message): + __slots__ = ("publisher_sid", "last_seq") + PUBLISHER_SID_FIELD_NUMBER: _ClassVar[int] + LAST_SEQ_FIELD_NUMBER: _ClassVar[int] + publisher_sid: str + last_seq: int + def __init__(self, publisher_sid: _Optional[str] = ..., last_seq: _Optional[int] = ...) -> None: ... + +class DataChannelInfo(_message.Message): + __slots__ = ("label", "id", "target") + LABEL_FIELD_NUMBER: _ClassVar[int] + ID_FIELD_NUMBER: _ClassVar[int] + TARGET_FIELD_NUMBER: _ClassVar[int] + label: str + id: int + target: SignalTarget + def __init__(self, label: _Optional[str] = ..., id: _Optional[int] = ..., target: _Optional[_Union[SignalTarget, str]] = ...) -> None: ... + +class SimulateScenario(_message.Message): + __slots__ = ("speaker_update", "node_failure", "migration", "server_leave", "switch_candidate_protocol", "subscriber_bandwidth", "disconnect_signal_on_resume", "disconnect_signal_on_resume_no_messages", "leave_request_full_reconnect") + SPEAKER_UPDATE_FIELD_NUMBER: _ClassVar[int] + NODE_FAILURE_FIELD_NUMBER: _ClassVar[int] + MIGRATION_FIELD_NUMBER: _ClassVar[int] + SERVER_LEAVE_FIELD_NUMBER: _ClassVar[int] + SWITCH_CANDIDATE_PROTOCOL_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBER_BANDWIDTH_FIELD_NUMBER: _ClassVar[int] + DISCONNECT_SIGNAL_ON_RESUME_FIELD_NUMBER: _ClassVar[int] + DISCONNECT_SIGNAL_ON_RESUME_NO_MESSAGES_FIELD_NUMBER: _ClassVar[int] + LEAVE_REQUEST_FULL_RECONNECT_FIELD_NUMBER: _ClassVar[int] + speaker_update: int + node_failure: bool + migration: bool + server_leave: bool + switch_candidate_protocol: CandidateProtocol + subscriber_bandwidth: int + disconnect_signal_on_resume: bool + disconnect_signal_on_resume_no_messages: bool + leave_request_full_reconnect: bool + def __init__(self, speaker_update: _Optional[int] = ..., node_failure: bool = ..., migration: bool = ..., server_leave: bool = ..., switch_candidate_protocol: _Optional[_Union[CandidateProtocol, str]] = ..., subscriber_bandwidth: _Optional[int] = ..., disconnect_signal_on_resume: bool = ..., disconnect_signal_on_resume_no_messages: bool = ..., leave_request_full_reconnect: bool = ...) -> None: ... + +class Ping(_message.Message): + __slots__ = ("timestamp", "rtt") + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + RTT_FIELD_NUMBER: _ClassVar[int] + timestamp: int + rtt: int + def __init__(self, timestamp: _Optional[int] = ..., rtt: _Optional[int] = ...) -> None: ... + +class Pong(_message.Message): + __slots__ = ("last_ping_timestamp", "timestamp") + LAST_PING_TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + TIMESTAMP_FIELD_NUMBER: _ClassVar[int] + last_ping_timestamp: int + timestamp: int + def __init__(self, last_ping_timestamp: _Optional[int] = ..., timestamp: _Optional[int] = ...) -> None: ... + +class RegionSettings(_message.Message): + __slots__ = ("regions",) + REGIONS_FIELD_NUMBER: _ClassVar[int] + regions: _containers.RepeatedCompositeFieldContainer[RegionInfo] + def __init__(self, regions: _Optional[_Iterable[_Union[RegionInfo, _Mapping]]] = ...) -> None: ... + +class RegionInfo(_message.Message): + __slots__ = ("region", "url", "distance") + REGION_FIELD_NUMBER: _ClassVar[int] + URL_FIELD_NUMBER: _ClassVar[int] + DISTANCE_FIELD_NUMBER: _ClassVar[int] + region: str + url: str + distance: int + def __init__(self, region: _Optional[str] = ..., url: _Optional[str] = ..., distance: _Optional[int] = ...) -> None: ... + +class SubscriptionResponse(_message.Message): + __slots__ = ("track_sid", "err") + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + ERR_FIELD_NUMBER: _ClassVar[int] + track_sid: str + err: _models.SubscriptionError + def __init__(self, track_sid: _Optional[str] = ..., err: _Optional[_Union[_models.SubscriptionError, str]] = ...) -> None: ... + +class RequestResponse(_message.Message): + __slots__ = ("request_id", "reason", "message", "trickle", "add_track", "mute", "update_metadata", "update_audio_track", "update_video_track", "publish_data_track", "unpublish_data_track") + class Reason(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + OK: _ClassVar[RequestResponse.Reason] + NOT_FOUND: _ClassVar[RequestResponse.Reason] + NOT_ALLOWED: _ClassVar[RequestResponse.Reason] + LIMIT_EXCEEDED: _ClassVar[RequestResponse.Reason] + QUEUED: _ClassVar[RequestResponse.Reason] + UNSUPPORTED_TYPE: _ClassVar[RequestResponse.Reason] + UNCLASSIFIED_ERROR: _ClassVar[RequestResponse.Reason] + INVALID_HANDLE: _ClassVar[RequestResponse.Reason] + INVALID_NAME: _ClassVar[RequestResponse.Reason] + DUPLICATE_HANDLE: _ClassVar[RequestResponse.Reason] + DUPLICATE_NAME: _ClassVar[RequestResponse.Reason] + INVALID_REQUEST: _ClassVar[RequestResponse.Reason] + OK: RequestResponse.Reason + NOT_FOUND: RequestResponse.Reason + NOT_ALLOWED: RequestResponse.Reason + LIMIT_EXCEEDED: RequestResponse.Reason + QUEUED: RequestResponse.Reason + UNSUPPORTED_TYPE: RequestResponse.Reason + UNCLASSIFIED_ERROR: RequestResponse.Reason + INVALID_HANDLE: RequestResponse.Reason + INVALID_NAME: RequestResponse.Reason + DUPLICATE_HANDLE: RequestResponse.Reason + DUPLICATE_NAME: RequestResponse.Reason + INVALID_REQUEST: RequestResponse.Reason + REQUEST_ID_FIELD_NUMBER: _ClassVar[int] + REASON_FIELD_NUMBER: _ClassVar[int] + MESSAGE_FIELD_NUMBER: _ClassVar[int] + TRICKLE_FIELD_NUMBER: _ClassVar[int] + ADD_TRACK_FIELD_NUMBER: _ClassVar[int] + MUTE_FIELD_NUMBER: _ClassVar[int] + UPDATE_METADATA_FIELD_NUMBER: _ClassVar[int] + UPDATE_AUDIO_TRACK_FIELD_NUMBER: _ClassVar[int] + UPDATE_VIDEO_TRACK_FIELD_NUMBER: _ClassVar[int] + PUBLISH_DATA_TRACK_FIELD_NUMBER: _ClassVar[int] + UNPUBLISH_DATA_TRACK_FIELD_NUMBER: _ClassVar[int] + request_id: int + reason: RequestResponse.Reason + message: str + trickle: TrickleRequest + add_track: AddTrackRequest + mute: MuteTrackRequest + update_metadata: UpdateParticipantMetadata + update_audio_track: UpdateLocalAudioTrack + update_video_track: UpdateLocalVideoTrack + publish_data_track: PublishDataTrackRequest + unpublish_data_track: UnpublishDataTrackRequest + def __init__(self, request_id: _Optional[int] = ..., reason: _Optional[_Union[RequestResponse.Reason, str]] = ..., message: _Optional[str] = ..., trickle: _Optional[_Union[TrickleRequest, _Mapping]] = ..., add_track: _Optional[_Union[AddTrackRequest, _Mapping]] = ..., mute: _Optional[_Union[MuteTrackRequest, _Mapping]] = ..., update_metadata: _Optional[_Union[UpdateParticipantMetadata, _Mapping]] = ..., update_audio_track: _Optional[_Union[UpdateLocalAudioTrack, _Mapping]] = ..., update_video_track: _Optional[_Union[UpdateLocalVideoTrack, _Mapping]] = ..., publish_data_track: _Optional[_Union[PublishDataTrackRequest, _Mapping]] = ..., unpublish_data_track: _Optional[_Union[UnpublishDataTrackRequest, _Mapping]] = ...) -> None: ... + +class TrackSubscribed(_message.Message): + __slots__ = ("track_sid",) + TRACK_SID_FIELD_NUMBER: _ClassVar[int] + track_sid: str + def __init__(self, track_sid: _Optional[str] = ...) -> None: ... + +class ConnectionSettings(_message.Message): + __slots__ = ("auto_subscribe", "adaptive_stream", "subscriber_allow_pause", "disable_ice_lite", "auto_subscribe_data_track") + AUTO_SUBSCRIBE_FIELD_NUMBER: _ClassVar[int] + ADAPTIVE_STREAM_FIELD_NUMBER: _ClassVar[int] + SUBSCRIBER_ALLOW_PAUSE_FIELD_NUMBER: _ClassVar[int] + DISABLE_ICE_LITE_FIELD_NUMBER: _ClassVar[int] + AUTO_SUBSCRIBE_DATA_TRACK_FIELD_NUMBER: _ClassVar[int] + auto_subscribe: bool + adaptive_stream: bool + subscriber_allow_pause: bool + disable_ice_lite: bool + auto_subscribe_data_track: bool + def __init__(self, auto_subscribe: bool = ..., adaptive_stream: bool = ..., subscriber_allow_pause: bool = ..., disable_ice_lite: bool = ..., auto_subscribe_data_track: bool = ...) -> None: ... + +class JoinRequest(_message.Message): + __slots__ = ("client_info", "connection_settings", "metadata", "participant_attributes", "add_track_requests", "publisher_offer", "reconnect", "reconnect_reason", "participant_sid", "sync_state") + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CLIENT_INFO_FIELD_NUMBER: _ClassVar[int] + CONNECTION_SETTINGS_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ADD_TRACK_REQUESTS_FIELD_NUMBER: _ClassVar[int] + PUBLISHER_OFFER_FIELD_NUMBER: _ClassVar[int] + RECONNECT_FIELD_NUMBER: _ClassVar[int] + RECONNECT_REASON_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_SID_FIELD_NUMBER: _ClassVar[int] + SYNC_STATE_FIELD_NUMBER: _ClassVar[int] + client_info: _models.ClientInfo + connection_settings: ConnectionSettings + metadata: str + participant_attributes: _containers.ScalarMap[str, str] + add_track_requests: _containers.RepeatedCompositeFieldContainer[AddTrackRequest] + publisher_offer: SessionDescription + reconnect: bool + reconnect_reason: _models.ReconnectReason + participant_sid: str + sync_state: SyncState + def __init__(self, client_info: _Optional[_Union[_models.ClientInfo, _Mapping]] = ..., connection_settings: _Optional[_Union[ConnectionSettings, _Mapping]] = ..., metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., add_track_requests: _Optional[_Iterable[_Union[AddTrackRequest, _Mapping]]] = ..., publisher_offer: _Optional[_Union[SessionDescription, _Mapping]] = ..., reconnect: bool = ..., reconnect_reason: _Optional[_Union[_models.ReconnectReason, str]] = ..., participant_sid: _Optional[str] = ..., sync_state: _Optional[_Union[SyncState, _Mapping]] = ...) -> None: ... + +class WrappedJoinRequest(_message.Message): + __slots__ = ("compression", "join_request") + class Compression(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NONE: _ClassVar[WrappedJoinRequest.Compression] + GZIP: _ClassVar[WrappedJoinRequest.Compression] + NONE: WrappedJoinRequest.Compression + GZIP: WrappedJoinRequest.Compression + COMPRESSION_FIELD_NUMBER: _ClassVar[int] + JOIN_REQUEST_FIELD_NUMBER: _ClassVar[int] + compression: WrappedJoinRequest.Compression + join_request: bytes + def __init__(self, compression: _Optional[_Union[WrappedJoinRequest.Compression, str]] = ..., join_request: _Optional[bytes] = ...) -> None: ... + +class MediaSectionsRequirement(_message.Message): + __slots__ = ("num_audios", "num_videos") + NUM_AUDIOS_FIELD_NUMBER: _ClassVar[int] + NUM_VIDEOS_FIELD_NUMBER: _ClassVar[int] + num_audios: int + num_videos: int + def __init__(self, num_audios: _Optional[int] = ..., num_videos: _Optional[int] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/sip.py b/livekit-protocol/livekit/protocol/sip.py index 7106c3cc..695153f9 100644 --- a/livekit-protocol/livekit/protocol/sip.py +++ b/livekit-protocol/livekit/protocol/sip.py @@ -1,105 +1,386 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_sip.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_sip.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() +from google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from . import models as _models_ +from . import room as _room_ +from .logger_pb import options as logger_dot_options__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11livekit_sip.proto\x12\x07livekit\"\xaf\x02\n\x15\x43reateSIPTrunkRequest\x12\x19\n\x11inbound_addresses\x18\x01 \x03(\t\x12\x18\n\x10outbound_address\x18\x02 \x01(\t\x12\x17\n\x0foutbound_number\x18\x03 \x01(\t\x12!\n\x15inbound_numbers_regex\x18\x04 \x03(\tB\x02\x18\x01\x12\x17\n\x0finbound_numbers\x18\t \x03(\t\x12\x18\n\x10inbound_username\x18\x05 \x01(\t\x12\x18\n\x10inbound_password\x18\x06 \x01(\t\x12\x19\n\x11outbound_username\x18\x07 \x01(\t\x12\x19\n\x11outbound_password\x18\x08 \x01(\t\x12\x0c\n\x04name\x18\n \x01(\t\x12\x10\n\x08metadata\x18\x0b \x01(\t:\x02\x18\x01\"\xdb\x03\n\x0cSIPTrunkInfo\x12\x14\n\x0csip_trunk_id\x18\x01 \x01(\t\x12-\n\x04kind\x18\x0e \x01(\x0e\x32\x1f.livekit.SIPTrunkInfo.TrunkKind\x12\x19\n\x11inbound_addresses\x18\x02 \x03(\t\x12\x18\n\x10outbound_address\x18\x03 \x01(\t\x12\x17\n\x0foutbound_number\x18\x04 \x01(\t\x12(\n\ttransport\x18\r \x01(\x0e\x32\x15.livekit.SIPTransport\x12!\n\x15inbound_numbers_regex\x18\x05 \x03(\tB\x02\x18\x01\x12\x17\n\x0finbound_numbers\x18\n \x03(\t\x12\x18\n\x10inbound_username\x18\x06 \x01(\t\x12\x18\n\x10inbound_password\x18\x07 \x01(\t\x12\x19\n\x11outbound_username\x18\x08 \x01(\t\x12\x19\n\x11outbound_password\x18\t \x01(\t\x12\x0c\n\x04name\x18\x0b \x01(\t\x12\x10\n\x08metadata\x18\x0c \x01(\t\"D\n\tTrunkKind\x12\x10\n\x0cTRUNK_LEGACY\x10\x00\x12\x11\n\rTRUNK_INBOUND\x10\x01\x12\x12\n\x0eTRUNK_OUTBOUND\x10\x02:\x02\x18\x01\"K\n\x1c\x43reateSIPInboundTrunkRequest\x12+\n\x05trunk\x18\x01 \x01(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\"\xbe\x01\n\x13SIPInboundTrunkInfo\x12\x14\n\x0csip_trunk_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08metadata\x18\x03 \x01(\t\x12\x0f\n\x07numbers\x18\x04 \x03(\t\x12\x19\n\x11\x61llowed_addresses\x18\x05 \x03(\t\x12\x17\n\x0f\x61llowed_numbers\x18\x06 \x03(\t\x12\x15\n\rauth_username\x18\x07 \x01(\t\x12\x15\n\rauth_password\x18\x08 \x01(\t\"M\n\x1d\x43reateSIPOutboundTrunkRequest\x12,\n\x05trunk\x18\x01 \x01(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\"\xc6\x01\n\x14SIPOutboundTrunkInfo\x12\x14\n\x0csip_trunk_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08metadata\x18\x03 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x04 \x01(\t\x12(\n\ttransport\x18\x05 \x01(\x0e\x32\x15.livekit.SIPTransport\x12\x0f\n\x07numbers\x18\x06 \x03(\t\x12\x15\n\rauth_username\x18\x07 \x01(\t\x12\x15\n\rauth_password\x18\x08 \x01(\t\"\x19\n\x13ListSIPTrunkRequest:\x02\x18\x01\"@\n\x14ListSIPTrunkResponse\x12$\n\x05items\x18\x01 \x03(\x0b\x32\x15.livekit.SIPTrunkInfo:\x02\x18\x01\"\x1c\n\x1aListSIPInboundTrunkRequest\"J\n\x1bListSIPInboundTrunkResponse\x12+\n\x05items\x18\x01 \x03(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\"\x1d\n\x1bListSIPOutboundTrunkRequest\"L\n\x1cListSIPOutboundTrunkResponse\x12,\n\x05items\x18\x01 \x03(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\"-\n\x15\x44\x65leteSIPTrunkRequest\x12\x14\n\x0csip_trunk_id\x18\x01 \x01(\t\"7\n\x15SIPDispatchRuleDirect\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x0b\n\x03pin\x18\x02 \x01(\t\"=\n\x19SIPDispatchRuleIndividual\x12\x13\n\x0broom_prefix\x18\x01 \x01(\t\x12\x0b\n\x03pin\x18\x02 \x01(\t\"\xa1\x01\n\x0fSIPDispatchRule\x12>\n\x14\x64ispatch_rule_direct\x18\x01 \x01(\x0b\x32\x1e.livekit.SIPDispatchRuleDirectH\x00\x12\x46\n\x18\x64ispatch_rule_individual\x18\x02 \x01(\x0b\x32\".livekit.SIPDispatchRuleIndividualH\x00\x42\x06\n\x04rule\"\xab\x02\n\x1c\x43reateSIPDispatchRuleRequest\x12&\n\x04rule\x18\x01 \x01(\x0b\x32\x18.livekit.SIPDispatchRule\x12\x11\n\ttrunk_ids\x18\x02 \x03(\t\x12\x19\n\x11hide_phone_number\x18\x03 \x01(\x08\x12\x17\n\x0finbound_numbers\x18\x06 \x03(\t\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x10\n\x08metadata\x18\x05 \x01(\t\x12I\n\nattributes\x18\x07 \x03(\x0b\x32\x35.livekit.CreateSIPDispatchRuleRequest.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xb7\x02\n\x13SIPDispatchRuleInfo\x12\x1c\n\x14sip_dispatch_rule_id\x18\x01 \x01(\t\x12&\n\x04rule\x18\x02 \x01(\x0b\x32\x18.livekit.SIPDispatchRule\x12\x11\n\ttrunk_ids\x18\x03 \x03(\t\x12\x19\n\x11hide_phone_number\x18\x04 \x01(\x08\x12\x17\n\x0finbound_numbers\x18\x07 \x03(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x10\n\x08metadata\x18\x06 \x01(\t\x12@\n\nattributes\x18\x08 \x03(\x0b\x32,.livekit.SIPDispatchRuleInfo.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x1c\n\x1aListSIPDispatchRuleRequest\"J\n\x1bListSIPDispatchRuleResponse\x12+\n\x05items\x18\x01 \x03(\x0b\x32\x1c.livekit.SIPDispatchRuleInfo\"<\n\x1c\x44\x65leteSIPDispatchRuleRequest\x12\x1c\n\x14sip_dispatch_rule_id\x18\x01 \x01(\t\"\x90\x03\n\x1b\x43reateSIPParticipantRequest\x12\x14\n\x0csip_trunk_id\x18\x01 \x01(\t\x12\x13\n\x0bsip_call_to\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12\x18\n\x10participant_name\x18\x07 \x01(\t\x12\x1c\n\x14participant_metadata\x18\x08 \x01(\t\x12_\n\x16participant_attributes\x18\t \x03(\x0b\x32?.livekit.CreateSIPParticipantRequest.ParticipantAttributesEntry\x12\x0c\n\x04\x64tmf\x18\x05 \x01(\t\x12\x15\n\rplay_ringtone\x18\x06 \x01(\x08\x12\x19\n\x11hide_phone_number\x18\n \x01(\x08\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"r\n\x12SIPParticipantInfo\x12\x16\n\x0eparticipant_id\x18\x01 \x01(\t\x12\x1c\n\x14participant_identity\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x13\n\x0bsip_call_id\x18\x04 \x01(\t*k\n\x0cSIPTransport\x12\x16\n\x12SIP_TRANSPORT_AUTO\x10\x00\x12\x15\n\x11SIP_TRANSPORT_UDP\x10\x01\x12\x15\n\x11SIP_TRANSPORT_TCP\x10\x02\x12\x15\n\x11SIP_TRANSPORT_TLS\x10\x03\x32\xed\x07\n\x03SIP\x12L\n\x0e\x43reateSIPTrunk\x12\x1e.livekit.CreateSIPTrunkRequest\x1a\x15.livekit.SIPTrunkInfo\"\x03\x88\x02\x01\x12P\n\x0cListSIPTrunk\x12\x1c.livekit.ListSIPTrunkRequest\x1a\x1d.livekit.ListSIPTrunkResponse\"\x03\x88\x02\x01\x12\\\n\x15\x43reateSIPInboundTrunk\x12%.livekit.CreateSIPInboundTrunkRequest\x1a\x1c.livekit.SIPInboundTrunkInfo\x12_\n\x16\x43reateSIPOutboundTrunk\x12&.livekit.CreateSIPOutboundTrunkRequest\x1a\x1d.livekit.SIPOutboundTrunkInfo\x12`\n\x13ListSIPInboundTrunk\x12#.livekit.ListSIPInboundTrunkRequest\x1a$.livekit.ListSIPInboundTrunkResponse\x12\x63\n\x14ListSIPOutboundTrunk\x12$.livekit.ListSIPOutboundTrunkRequest\x1a%.livekit.ListSIPOutboundTrunkResponse\x12G\n\x0e\x44\x65leteSIPTrunk\x12\x1e.livekit.DeleteSIPTrunkRequest\x1a\x15.livekit.SIPTrunkInfo\x12\\\n\x15\x43reateSIPDispatchRule\x12%.livekit.CreateSIPDispatchRuleRequest\x1a\x1c.livekit.SIPDispatchRuleInfo\x12`\n\x13ListSIPDispatchRule\x12#.livekit.ListSIPDispatchRuleRequest\x1a$.livekit.ListSIPDispatchRuleResponse\x12\\\n\x15\x44\x65leteSIPDispatchRule\x12%.livekit.DeleteSIPDispatchRuleRequest\x1a\x1c.livekit.SIPDispatchRuleInfo\x12Y\n\x14\x43reateSIPParticipant\x12$.livekit.CreateSIPParticipantRequest\x1a\x1b.livekit.SIPParticipantInfoBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11livekit_sip.proto\x12\x07livekit\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a\x1bgoogle/protobuf/empty.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x14livekit_models.proto\x1a\x12livekit_room.proto\x1a\x14logger/options.proto\"A\n\tSIPStatus\x12$\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x16.livekit.SIPStatusCode\x12\x0e\n\x06status\x18\x02 \x01(\t\"\xe9\x02\n\x15\x43reateSIPTrunkRequest\x12\x19\n\x11inbound_addresses\x18\x01 \x03(\t\x12\x18\n\x10outbound_address\x18\x02 \x01(\t\x12\x17\n\x0foutbound_number\x18\x03 \x01(\t\x12!\n\x15inbound_numbers_regex\x18\x04 \x03(\tB\x02\x18\x01\x12\x17\n\x0finbound_numbers\x18\t \x03(\t\x12\x1d\n\x10inbound_username\x18\x05 \x01(\tB\x03\xc0P\x01\x12\x1d\n\x10inbound_password\x18\x06 \x01(\tB\x03\xc0P\x02\x12\x1e\n\x11outbound_username\x18\x07 \x01(\tB\x03\xc0P\x01\x12\x1e\n\x11outbound_password\x18\x08 \x01(\tB\x03\xc0P\x02\x12\x0c\n\x04name\x18\n \x01(\t\x12\x36\n\x08metadata\x18\x0b \x01(\tB$\xb2P\x1e\xc0P\x01:\x02\x18\x01\"&\n\x08SIPCodec\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04rate\x18\x02 \x01(\r\"\xdd\x01\n\x0eSIPMediaConfig\x12\x1a\n\x12only_listed_codecs\x18\x01 \x01(\x08\x12!\n\x06\x63odecs\x18\x02 \x03(\x0b\x32\x11.livekit.SIPCodec\x12\x34\n\nencryption\x18\x03 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionH\x00\x88\x01\x01\x12\x35\n\rmedia_timeout\x18\x04 \x01(\x0b\x32\x19.google.protobuf.DurationH\x01\x88\x01\x01\x42\r\n\x0b_encryptionB\x10\n\x0e_media_timeout\"g\n\x0cProviderInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12#\n\x04type\x18\x03 \x01(\x0e\x32\x15.livekit.ProviderType\x12\x18\n\x10prevent_transfer\x18\x04 \x01(\x08\"\xa4\x04\n\x0cSIPTrunkInfo\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12-\n\x04kind\x18\x0e \x01(\x0e\x32\x1f.livekit.SIPTrunkInfo.TrunkKind\x12\x19\n\x11inbound_addresses\x18\x02 \x03(\t\x12\x18\n\x10outbound_address\x18\x03 \x01(\t\x12\x17\n\x0foutbound_number\x18\x04 \x01(\t\x12(\n\ttransport\x18\r \x01(\x0e\x32\x15.livekit.SIPTransport\x12!\n\x15inbound_numbers_regex\x18\x05 \x03(\tB\x02\x18\x01\x12\x17\n\x0finbound_numbers\x18\n \x03(\t\x12\x1d\n\x10inbound_username\x18\x06 \x01(\tB\x03\xc0P\x01\x12\x1d\n\x10inbound_password\x18\x07 \x01(\tB\x03\xc0P\x02\x12\x1e\n\x11outbound_username\x18\x08 \x01(\tB\x03\xc0P\x01\x12\x1e\n\x11outbound_password\x18\t \x01(\tB\x03\xc0P\x02\x12\x0c\n\x04name\x18\x0b \x01(\t\x12\x36\n\x08metadata\x18\x0c \x01(\tB$\xb2P\x1e\xc0P\x01\"D\n\tTrunkKind\x12\x10\n\x0cTRUNK_LEGACY\x10\x00\x12\x11\n\rTRUNK_INBOUND\x10\x01\x12\x12\n\x0eTRUNK_OUTBOUND\x10\x02:\x02\x18\x01\"K\n\x1c\x43reateSIPInboundTrunkRequest\x12+\n\x05trunk\x18\x01 \x01(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\"\xb0\x01\n\x1cUpdateSIPInboundTrunkRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12/\n\x07replace\x18\x02 \x01(\x0b\x32\x1c.livekit.SIPInboundTrunkInfoH\x00\x12\x30\n\x06update\x18\x03 \x01(\x0b\x32\x1e.livekit.SIPInboundTrunkUpdateH\x00\x42\x08\n\x06\x61\x63tion\"\xbf\x08\n\x13SIPInboundTrunkInfo\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x36\n\x08metadata\x18\x03 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x0f\n\x07numbers\x18\x04 \x03(\t\x12\x19\n\x11\x61llowed_addresses\x18\x05 \x03(\t\x12\x17\n\x0f\x61llowed_numbers\x18\x06 \x03(\t\x12\x1a\n\rauth_username\x18\x07 \x01(\tB\x03\xc0P\x01\x12\x1a\n\rauth_password\x18\x08 \x01(\tB\x03\xc0P\x02\x12\x12\n\nauth_realm\x18\x13 \x01(\t\x12`\n\x07headers\x18\t \x03(\x0b\x32).livekit.SIPInboundTrunkInfo.HeadersEntryB$\xb2P\x1e\xc0P\x01\x12T\n\x15headers_to_attributes\x18\n \x03(\x0b\x32\x35.livekit.SIPInboundTrunkInfo.HeadersToAttributesEntry\x12T\n\x15\x61ttributes_to_headers\x18\x0e \x03(\x0b\x32\x35.livekit.SIPInboundTrunkInfo.AttributesToHeadersEntry\x12\x32\n\x0finclude_headers\x18\x0f \x01(\x0e\x32\x19.livekit.SIPHeaderOptions\x12\x32\n\x0fringing_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11max_call_duration\x18\x0c \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x15\n\rkrisp_enabled\x18\r \x01(\x08\x12\x39\n\x10media_encryption\x18\x10 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01\x12&\n\x05media\x18\x14 \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x12.\n\ncreated_at\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x12 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a:\n\x18HeadersToAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a:\n\x18\x41ttributesToHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x8c\x04\n\x15SIPInboundTrunkUpdate\x12$\n\x07numbers\x18\x01 \x01(\x0b\x32\x13.livekit.ListUpdate\x12.\n\x11\x61llowed_addresses\x18\x02 \x01(\x0b\x32\x13.livekit.ListUpdate\x12,\n\x0f\x61llowed_numbers\x18\x03 \x01(\x0b\x32\x13.livekit.ListUpdate\x12\x1f\n\rauth_username\x18\x04 \x01(\tB\x03\xc0P\x01H\x00\x88\x01\x01\x12\x1f\n\rauth_password\x18\x05 \x01(\tB\x03\xc0P\x02H\x01\x88\x01\x01\x12\x17\n\nauth_realm\x18\t \x01(\tH\x02\x88\x01\x01\x12\x11\n\x04name\x18\x06 \x01(\tH\x03\x88\x01\x01\x12;\n\x08metadata\x18\x07 \x01(\tB$\xb2P\x1e\xc0P\x01H\x04\x88\x01\x01\x12>\n\x10media_encryption\x18\x08 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01H\x05\x88\x01\x01\x12&\n\x05media\x18\n \x01(\x0b\x32\x17.livekit.SIPMediaConfigB\x10\n\x0e_auth_usernameB\x10\n\x0e_auth_passwordB\r\n\x0b_auth_realmB\x07\n\x05_nameB\x0b\n\t_metadataB\x13\n\x11_media_encryption\"M\n\x1d\x43reateSIPOutboundTrunkRequest\x12,\n\x05trunk\x18\x01 \x01(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\"\xb3\x01\n\x1dUpdateSIPOutboundTrunkRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12\x30\n\x07replace\x18\x02 \x01(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfoH\x00\x12\x31\n\x06update\x18\x03 \x01(\x0b\x32\x1f.livekit.SIPOutboundTrunkUpdateH\x00\x42\x08\n\x06\x61\x63tion\"\xe5\x07\n\x14SIPOutboundTrunkInfo\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x36\n\x08metadata\x18\x03 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x0f\n\x07\x61\x64\x64ress\x18\x04 \x01(\t\x12\x1b\n\x13\x64\x65stination_country\x18\x0e \x01(\t\x12(\n\ttransport\x18\x05 \x01(\x0e\x32\x15.livekit.SIPTransport\x12\x0f\n\x07numbers\x18\x06 \x03(\t\x12\x1a\n\rauth_username\x18\x07 \x01(\tB\x03\xc0P\x01\x12\x1a\n\rauth_password\x18\x08 \x01(\tB\x03\xc0P\x02\x12\x61\n\x07headers\x18\t \x03(\x0b\x32*.livekit.SIPOutboundTrunkInfo.HeadersEntryB$\xb2P\x1e\xc0P\x01\x12U\n\x15headers_to_attributes\x18\n \x03(\x0b\x32\x36.livekit.SIPOutboundTrunkInfo.HeadersToAttributesEntry\x12U\n\x15\x61ttributes_to_headers\x18\x0b \x03(\x0b\x32\x36.livekit.SIPOutboundTrunkInfo.AttributesToHeadersEntry\x12\x32\n\x0finclude_headers\x18\x0c \x01(\x0e\x32\x19.livekit.SIPHeaderOptions\x12\x39\n\x10media_encryption\x18\r \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01\x12&\n\x05media\x18\x12 \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x12\x11\n\tfrom_host\x18\x0f \x01(\t\x12.\n\ncreated_at\x18\x10 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x11 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a:\n\x18HeadersToAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a:\n\x18\x41ttributesToHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc6\x04\n\x16SIPOutboundTrunkUpdate\x12\x14\n\x07\x61\x64\x64ress\x18\x01 \x01(\tH\x00\x88\x01\x01\x12-\n\ttransport\x18\x02 \x01(\x0e\x32\x15.livekit.SIPTransportH\x01\x88\x01\x01\x12 \n\x13\x64\x65stination_country\x18\t \x01(\tH\x02\x88\x01\x01\x12$\n\x07numbers\x18\x03 \x01(\x0b\x32\x13.livekit.ListUpdate\x12\x1f\n\rauth_username\x18\x04 \x01(\tB\x03\xc0P\x01H\x03\x88\x01\x01\x12\x1f\n\rauth_password\x18\x05 \x01(\tB\x03\xc0P\x02H\x04\x88\x01\x01\x12\x11\n\x04name\x18\x06 \x01(\tH\x05\x88\x01\x01\x12;\n\x08metadata\x18\x07 \x01(\tB$\xb2P\x1e\xc0P\x01H\x06\x88\x01\x01\x12>\n\x10media_encryption\x18\x08 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01H\x07\x88\x01\x01\x12&\n\x05media\x18\x0b \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x12\x16\n\tfrom_host\x18\n \x01(\tH\x08\x88\x01\x01\x42\n\n\x08_addressB\x0c\n\n_transportB\x16\n\x14_destination_countryB\x10\n\x0e_auth_usernameB\x10\n\x0e_auth_passwordB\x07\n\x05_nameB\x0b\n\t_metadataB\x13\n\x11_media_encryptionB\x0c\n\n_from_host\"@\n\x19GetSIPInboundTrunkRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\"I\n\x1aGetSIPInboundTrunkResponse\x12+\n\x05trunk\x18\x01 \x01(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\"A\n\x1aGetSIPOutboundTrunkRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\"K\n\x1bGetSIPOutboundTrunkResponse\x12,\n\x05trunk\x18\x01 \x01(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\"<\n\x13ListSIPTrunkRequest\x12!\n\x04page\x18\x01 \x01(\x0b\x32\x13.livekit.Pagination:\x02\x18\x01\"@\n\x14ListSIPTrunkResponse\x12$\n\x05items\x18\x01 \x03(\x0b\x32\x15.livekit.SIPTrunkInfo:\x02\x18\x01\"c\n\x1aListSIPInboundTrunkRequest\x12!\n\x04page\x18\x03 \x01(\x0b\x32\x13.livekit.Pagination\x12\x11\n\ttrunk_ids\x18\x01 \x03(\t\x12\x0f\n\x07numbers\x18\x02 \x03(\t\"J\n\x1bListSIPInboundTrunkResponse\x12+\n\x05items\x18\x01 \x03(\x0b\x32\x1c.livekit.SIPInboundTrunkInfo\"d\n\x1bListSIPOutboundTrunkRequest\x12!\n\x04page\x18\x03 \x01(\x0b\x32\x13.livekit.Pagination\x12\x11\n\ttrunk_ids\x18\x01 \x03(\t\x12\x0f\n\x07numbers\x18\x02 \x03(\t\"L\n\x1cListSIPOutboundTrunkResponse\x12,\n\x05items\x18\x01 \x03(\x0b\x32\x1d.livekit.SIPOutboundTrunkInfo\"<\n\x15\x44\x65leteSIPTrunkRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\"7\n\x15SIPDispatchRuleDirect\x12\x11\n\troom_name\x18\x01 \x01(\t\x12\x0b\n\x03pin\x18\x02 \x01(\t\"T\n\x19SIPDispatchRuleIndividual\x12\x13\n\x0broom_prefix\x18\x01 \x01(\t\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x15\n\rno_randomness\x18\x03 \x01(\x08\"L\n\x15SIPDispatchRuleCallee\x12\x13\n\x0broom_prefix\x18\x01 \x01(\t\x12\x0b\n\x03pin\x18\x02 \x01(\t\x12\x11\n\trandomize\x18\x03 \x01(\x08\"\xe1\x01\n\x0fSIPDispatchRule\x12>\n\x14\x64ispatch_rule_direct\x18\x01 \x01(\x0b\x32\x1e.livekit.SIPDispatchRuleDirectH\x00\x12\x46\n\x18\x64ispatch_rule_individual\x18\x02 \x01(\x0b\x32\".livekit.SIPDispatchRuleIndividualH\x00\x12>\n\x14\x64ispatch_rule_callee\x18\x03 \x01(\x0b\x32\x1e.livekit.SIPDispatchRuleCalleeH\x00\x42\x06\n\x04rule\"\x92\x04\n\x1c\x43reateSIPDispatchRuleRequest\x12\x33\n\rdispatch_rule\x18\n \x01(\x0b\x32\x1c.livekit.SIPDispatchRuleInfo\x12*\n\x04rule\x18\x01 \x01(\x0b\x32\x18.livekit.SIPDispatchRuleB\x02\x18\x01\x12\x15\n\ttrunk_ids\x18\x02 \x03(\tB\x02\x18\x01\x12\x1d\n\x11hide_phone_number\x18\x03 \x01(\x08\x42\x02\x18\x01\x12\x1b\n\x0finbound_numbers\x18\x06 \x03(\tB\x02\x18\x01\x12\x10\n\x04name\x18\x04 \x01(\tB\x02\x18\x01\x12\x38\n\x08metadata\x18\x05 \x01(\tB&\x18\x01\xb2P\x1e\xc0P\x01\x12q\n\nattributes\x18\x07 \x03(\x0b\x32\x35.livekit.CreateSIPDispatchRuleRequest.AttributesEntryB&\x18\x01\xb2P\x1e\xc0P\x01\x12\x17\n\x0broom_preset\x18\x08 \x01(\tB\x02\x18\x01\x12\x33\n\x0broom_config\x18\t \x01(\x0b\x32\x1a.livekit.RoomConfigurationB\x02\x18\x01\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xbf\x01\n\x1cUpdateSIPDispatchRuleRequest\x12\x32\n\x14sip_dispatch_rule_id\x18\x01 \x01(\tB\x14\xbaP\x11sipDispatchRuleID\x12/\n\x07replace\x18\x02 \x01(\x0b\x32\x1c.livekit.SIPDispatchRuleInfoH\x00\x12\x30\n\x06update\x18\x03 \x01(\x0b\x32\x1e.livekit.SIPDispatchRuleUpdateH\x00\x42\x08\n\x06\x61\x63tion\"\xca\x05\n\x13SIPDispatchRuleInfo\x12\x32\n\x14sip_dispatch_rule_id\x18\x01 \x01(\tB\x14\xbaP\x11sipDispatchRuleID\x12&\n\x04rule\x18\x02 \x01(\x0b\x32\x18.livekit.SIPDispatchRule\x12\x11\n\ttrunk_ids\x18\x03 \x03(\t\x12\x19\n\x11hide_phone_number\x18\x04 \x01(\x08\x12\x17\n\x0finbound_numbers\x18\x07 \x03(\t\x12\x0f\n\x07numbers\x18\r \x03(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x36\n\x08metadata\x18\x06 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x66\n\nattributes\x18\x08 \x03(\x0b\x32,.livekit.SIPDispatchRuleInfo.AttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x13\n\x0broom_preset\x18\t \x01(\t\x12/\n\x0broom_config\x18\n \x01(\x0b\x32\x1a.livekit.RoomConfiguration\x12&\n\x05media\x18\x10 \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x12\x15\n\rkrisp_enabled\x18\x0b \x01(\x08\x12\x39\n\x10media_encryption\x18\x0c \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01\x12.\n\ncreated_at\x18\x0e \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12.\n\nupdated_at\x18\x0f \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe7\x03\n\x15SIPDispatchRuleUpdate\x12&\n\ttrunk_ids\x18\x01 \x01(\x0b\x32\x13.livekit.ListUpdate\x12&\n\x04rule\x18\x02 \x01(\x0b\x32\x18.livekit.SIPDispatchRule\x12\x11\n\x04name\x18\x03 \x01(\tH\x00\x88\x01\x01\x12;\n\x08metadata\x18\x04 \x01(\tB$\xb2P\x1e\xc0P\x01H\x01\x88\x01\x01\x12h\n\nattributes\x18\x05 \x03(\x0b\x32..livekit.SIPDispatchRuleUpdate.AttributesEntryB$\xb2P\x1e\xc0P\x01\x12>\n\x10media_encryption\x18\x06 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01H\x02\x88\x01\x01\x12&\n\x05media\x18\x07 \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x07\n\x05_nameB\x0b\n\t_metadataB\x13\n\x11_media_encryption\"m\n\x1aListSIPDispatchRuleRequest\x12!\n\x04page\x18\x03 \x01(\x0b\x32\x13.livekit.Pagination\x12\x19\n\x11\x64ispatch_rule_ids\x18\x01 \x03(\t\x12\x11\n\ttrunk_ids\x18\x02 \x03(\t\"J\n\x1bListSIPDispatchRuleResponse\x12+\n\x05items\x18\x01 \x03(\x0b\x32\x1c.livekit.SIPDispatchRuleInfo\"R\n\x1c\x44\x65leteSIPDispatchRuleRequest\x12\x32\n\x14sip_dispatch_rule_id\x18\x01 \x01(\tB\x14\xbaP\x11sipDispatchRuleID\"\xd7\x03\n\x11SIPOutboundConfig\x12\x10\n\x08hostname\x18\x01 \x01(\t\x12\x1b\n\x13\x64\x65stination_country\x18\x07 \x01(\t\x12(\n\ttransport\x18\x02 \x01(\x0e\x32\x15.livekit.SIPTransport\x12\x1a\n\rauth_username\x18\x03 \x01(\tB\x03\xc0P\x01\x12\x1a\n\rauth_password\x18\x04 \x01(\tB\x03\xc0P\x02\x12R\n\x15headers_to_attributes\x18\x05 \x03(\x0b\x32\x33.livekit.SIPOutboundConfig.HeadersToAttributesEntry\x12R\n\x15\x61ttributes_to_headers\x18\x06 \x03(\x0b\x32\x33.livekit.SIPOutboundConfig.AttributesToHeadersEntry\x12\x11\n\tfrom_host\x18\x08 \x01(\t\x1a:\n\x18HeadersToAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a:\n\x18\x41ttributesToHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xdd\n\n\x1b\x43reateSIPParticipantRequest\x12#\n\x0csip_trunk_id\x18\x01 \x01(\tB\r\xbaP\nsipTrunkID\x12)\n\x05trunk\x18\x14 \x01(\x0b\x32\x1a.livekit.SIPOutboundConfig\x12\x30\n\x0fsip_request_uri\x18\x18 \x01(\x0b\x32\x17.livekit.SIPRequestDest\x12,\n\rsip_to_header\x18\x19 \x01(\x0b\x32\x15.livekit.SIPNamedDest\x12.\n\x0fsip_from_header\x18\x1a \x01(\x0b\x32\x15.livekit.SIPNamedDest\x12\x13\n\x0bsip_call_to\x18\x02 \x01(\t\x12\x12\n\nsip_number\x18\x0f \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1c\n\x14participant_identity\x18\x04 \x01(\t\x12>\n\x10participant_name\x18\x07 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x42\n\x14participant_metadata\x18\x08 \x01(\tB$\xb2P\x1e\xc0P\x01\x12\x85\x01\n\x16participant_attributes\x18\t \x03(\x0b\x32?.livekit.CreateSIPParticipantRequest.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12\x0c\n\x04\x64tmf\x18\x05 \x01(\t\x12\x19\n\rplay_ringtone\x18\x06 \x01(\x08\x42\x02\x18\x01\x12\x15\n\rplay_dialtone\x18\r \x01(\x08\x12\x19\n\x11hide_phone_number\x18\n \x01(\x08\x12h\n\x07headers\x18\x10 \x03(\x0b\x32\x31.livekit.CreateSIPParticipantRequest.HeadersEntryB$\xb2P\x1e\xc0P\x01\x12\x32\n\x0finclude_headers\x18\x11 \x01(\x0e\x32\x19.livekit.SIPHeaderOptions\x12\x32\n\x0fringing_timeout\x18\x0b \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x34\n\x11max_call_duration\x18\x0c \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x15\n\rkrisp_enabled\x18\x0e \x01(\x08\x12\x39\n\x10media_encryption\x18\x12 \x01(\x0e\x32\x1b.livekit.SIPMediaEncryptionB\x02\x18\x01\x12&\n\x05media\x18\x17 \x01(\x0b\x32\x17.livekit.SIPMediaConfig\x12\x1b\n\x13wait_until_answered\x18\x13 \x01(\x08\x12?\n\x0c\x64isplay_name\x18\x15 \x01(\tB$\xb2P\x1e\xc0P\x01H\x00\x88\x01\x01\x12.\n\x0b\x64\x65stination\x18\x16 \x01(\x0b\x32\x14.livekit.DestinationH\x01\x88\x01\x01\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x0f\n\r_display_nameB\x0e\n\x0c_destination\"\x92\x01\n\x12SIPParticipantInfo\x12(\n\x0eparticipant_id\x18\x01 \x01(\tB\x10\xbaP\rparticipantID\x12\x1c\n\x14participant_identity\x18\x02 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12!\n\x0bsip_call_id\x18\x04 \x01(\tB\x0c\xbaP\tsipCallID\"\xcc\x02\n\x1dTransferSIPParticipantRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\troom_name\x18\x02 \x01(\t\x12\x13\n\x0btransfer_to\x18\x03 \x01(\t\x12\x15\n\rplay_dialtone\x18\x04 \x01(\x08\x12j\n\x07headers\x18\x05 \x03(\x0b\x32\x33.livekit.TransferSIPParticipantRequest.HeadersEntryB$\xb2P\x1e\xc0P\x01\x12\x32\n\x0fringing_timeout\x18\x06 \x01(\x0b\x32\x19.google.protobuf.Duration\x1a.\n\x0cHeadersEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x99\x08\n\x0bSIPCallInfo\x12\x1a\n\x07\x63\x61ll_id\x18\x01 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x1c\n\x08trunk_id\x18\x02 \x01(\tB\n\xbaP\x07trunkID\x12+\n\x10\x64ispatch_rule_id\x18\x10 \x01(\tB\x11\xbaP\x0e\x64ispatchRuleID\x12\x0e\n\x06region\x18\x11 \x01(\t\x12\x11\n\troom_name\x18\x03 \x01(\t\x12\x1a\n\x07room_id\x18\x04 \x01(\tB\t\xbaP\x06roomID\x12\x1c\n\x14participant_identity\x18\x05 \x01(\t\x12u\n\x16participant_attributes\x18\x12 \x03(\x0b\x32/.livekit.SIPCallInfo.ParticipantAttributesEntryB$\xb2P\x1e\xc0P\x01\x12!\n\x08\x66rom_uri\x18\x06 \x01(\x0b\x32\x0f.livekit.SIPUri\x12\x1f\n\x06to_uri\x18\x07 \x01(\x0b\x32\x0f.livekit.SIPUri\x12\x16\n\ncreated_at\x18\t \x01(\x03\x42\x02\x18\x01\x12\x16\n\nstarted_at\x18\n \x01(\x03\x42\x02\x18\x01\x12\x14\n\x08\x65nded_at\x18\x0b \x01(\x03\x42\x02\x18\x01\x12-\n\x10\x65nabled_features\x18\x0e \x03(\x0e\x32\x13.livekit.SIPFeature\x12\x31\n\x0e\x63\x61ll_direction\x18\x0f \x01(\x0e\x32\x19.livekit.SIPCallDirection\x12+\n\x0b\x63\x61ll_status\x18\x08 \x01(\x0e\x32\x16.livekit.SIPCallStatus\x12\x15\n\rcreated_at_ns\x18\x16 \x01(\x03\x12\x15\n\rstarted_at_ns\x18\x17 \x01(\x03\x12\x13\n\x0b\x65nded_at_ns\x18\x18 \x01(\x03\x12\x34\n\x11\x64isconnect_reason\x18\x0c \x01(\x0e\x32\x19.livekit.DisconnectReason\x12\r\n\x05\x65rror\x18\r \x01(\t\x12,\n\x10\x63\x61ll_status_code\x18\x13 \x01(\x0b\x32\x12.livekit.SIPStatus\x12\x13\n\x0b\x61udio_codec\x18\x14 \x01(\t\x12\x18\n\x10media_encryption\x18\x15 \x01(\t\x12\x16\n\x0epcap_file_link\x18\x19 \x01(\t\x12*\n\x0c\x63\x61ll_context\x18\x1a \x03(\x0b\x32\x14.google.protobuf.Any\x12,\n\rprovider_info\x18\x1b \x01(\x0b\x32\x15.livekit.ProviderInfo\x12!\n\x0bsip_call_id\x18\x1c \x01(\tB\x0c\xbaP\tsipCallID\x1a<\n\x1aParticipantAttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa0\x02\n\x0fSIPTransferInfo\x12\"\n\x0btransfer_id\x18\x01 \x01(\tB\r\xbaP\ntransferID\x12\x1a\n\x07\x63\x61ll_id\x18\x02 \x01(\tB\t\xbaP\x06\x63\x61llID\x12\x13\n\x0btransfer_to\x18\x03 \x01(\t\x12 \n\x18transfer_initiated_at_ns\x18\x04 \x01(\x03\x12 \n\x18transfer_completed_at_ns\x18\x05 \x01(\x03\x12\x33\n\x0ftransfer_status\x18\x06 \x01(\x0e\x32\x1a.livekit.SIPTransferStatus\x12\r\n\x05\x65rror\x18\x07 \x01(\t\x12\x30\n\x14transfer_status_code\x18\x08 \x01(\x0b\x32\x12.livekit.SIPStatus\"h\n\x06SIPUri\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x0c\n\x04host\x18\x02 \x01(\t\x12\n\n\x02ip\x18\x03 \x01(\t\x12\x0c\n\x04port\x18\x04 \x01(\r\x12(\n\ttransport\x18\x05 \x01(\x0e\x32\x15.livekit.SIPTransport\"I\n\x0eSIPRequestDest\x12\r\n\x03raw\x18\x01 \x01(\tH\x00\x12!\n\x06values\x18\x02 \x01(\x0b\x32\x0f.livekit.SIPUriH\x00\x42\x05\n\x03uri\"]\n\x0cSIPNamedDest\x12\x14\n\x0c\x64isplay_name\x18\x03 \x01(\t\x12\r\n\x03raw\x18\x01 \x01(\tH\x00\x12!\n\x06values\x18\x02 \x01(\x0b\x32\x0f.livekit.SIPUriH\x00\x42\x05\n\x03uri\"<\n\x0b\x44\x65stination\x12\x0c\n\x04\x63ity\x18\x01 \x01(\t\x12\x0f\n\x07\x63ountry\x18\x02 \x01(\t\x12\x0e\n\x06region\x18\x03 \x01(\t*\xde\x14\n\rSIPStatusCode\x12\x16\n\x12SIP_STATUS_UNKNOWN\x10\x00\x12\x15\n\x11SIP_STATUS_TRYING\x10\x64\x12\x17\n\x12SIP_STATUS_RINGING\x10\xb4\x01\x12!\n\x1cSIP_STATUS_CALL_IS_FORWARDED\x10\xb5\x01\x12\x16\n\x11SIP_STATUS_QUEUED\x10\xb6\x01\x12 \n\x1bSIP_STATUS_SESSION_PROGRESS\x10\xb7\x01\x12\'\n\"SIP_STATUS_EARLY_DIALOG_TERMINATED\x10\xc7\x01\x12\x12\n\rSIP_STATUS_OK\x10\xc8\x01\x12\x18\n\x13SIP_STATUS_ACCEPTED\x10\xca\x01\x12\x1f\n\x1aSIP_STATUS_NO_NOTIFICATION\x10\xcc\x01\x12 \n\x1bSIP_STATUS_MULTIPLE_CHOICES\x10\xac\x02\x12!\n\x1cSIP_STATUS_MOVED_PERMANENTLY\x10\xad\x02\x12!\n\x1cSIP_STATUS_MOVED_TEMPORARILY\x10\xae\x02\x12\x19\n\x14SIP_STATUS_USE_PROXY\x10\xb1\x02\x12#\n\x1eSIP_STATUS_ALTERNATIVE_SERVICE\x10\xfc\x02\x12\x1b\n\x16SIP_STATUS_BAD_REQUEST\x10\x90\x03\x12\x1c\n\x17SIP_STATUS_UNAUTHORIZED\x10\x91\x03\x12 \n\x1bSIP_STATUS_PAYMENT_REQUIRED\x10\x92\x03\x12\x19\n\x14SIP_STATUS_FORBIDDEN\x10\x93\x03\x12\x18\n\x13SIP_STATUS_NOTFOUND\x10\x94\x03\x12\"\n\x1dSIP_STATUS_METHOD_NOT_ALLOWED\x10\x95\x03\x12\x1e\n\x19SIP_STATUS_NOT_ACCEPTABLE\x10\x96\x03\x12#\n\x1eSIP_STATUS_PROXY_AUTH_REQUIRED\x10\x97\x03\x12\x1f\n\x1aSIP_STATUS_REQUEST_TIMEOUT\x10\x98\x03\x12\x18\n\x13SIP_STATUS_CONFLICT\x10\x99\x03\x12\x14\n\x0fSIP_STATUS_GONE\x10\x9a\x03\x12\x1f\n\x1aSIP_STATUS_LENGTH_REQUIRED\x10\x9b\x03\x12*\n%SIP_STATUS_CONDITIONAL_REQUEST_FAILED\x10\x9c\x03\x12(\n#SIP_STATUS_REQUEST_ENTITY_TOO_LARGE\x10\x9d\x03\x12$\n\x1fSIP_STATUS_REQUEST_URI_TOO_LONG\x10\x9e\x03\x12&\n!SIP_STATUS_UNSUPPORTED_MEDIA_TYPE\x10\x9f\x03\x12/\n*SIP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE\x10\xa0\x03\x12)\n$SIP_STATUS_UNKNOWN_RESOURCE_PRIORITY\x10\xa1\x03\x12\x1d\n\x18SIP_STATUS_BAD_EXTENSION\x10\xa4\x03\x12\"\n\x1dSIP_STATUS_EXTENSION_REQUIRED\x10\xa5\x03\x12*\n%SIP_STATUS_SESSION_INTERVAL_TOO_SMALL\x10\xa6\x03\x12\"\n\x1dSIP_STATUS_INTERVAL_TOO_BRIEF\x10\xa7\x03\x12(\n#SIP_STATUS_BAD_LOCATION_INFORMATION\x10\xa8\x03\x12!\n\x1cSIP_STATUS_BAD_ALERT_MESSAGE\x10\xa9\x03\x12#\n\x1eSIP_STATUS_USE_IDENTITY_HEADER\x10\xac\x03\x12)\n$SIP_STATUS_PROVIDE_REFERRER_IDENTITY\x10\xad\x03\x12\x1b\n\x16SIP_STATUS_FLOW_FAILED\x10\xae\x03\x12$\n\x1fSIP_STATUS_ANONYMITY_DISALLOWED\x10\xb1\x03\x12!\n\x1cSIP_STATUS_BAD_IDENTITY_INFO\x10\xb4\x03\x12\'\n\"SIP_STATUS_UNSUPPORTED_CERTIFICATE\x10\xb5\x03\x12\'\n\"SIP_STATUS_INVALID_IDENTITY_HEADER\x10\xb6\x03\x12\x30\n+SIP_STATUS_FIRST_HOP_LACKS_OUTBOUND_SUPPORT\x10\xb7\x03\x12$\n\x1fSIP_STATUS_MAX_BREADTH_EXCEEDED\x10\xb8\x03\x12 \n\x1bSIP_STATUS_BAD_INFO_PACKAGE\x10\xd5\x03\x12\x1e\n\x19SIP_STATUS_CONSENT_NEEDED\x10\xd6\x03\x12\'\n\"SIP_STATUS_TEMPORARILY_UNAVAILABLE\x10\xe0\x03\x12\x30\n+SIP_STATUS_CALL_TRANSACTION_DOES_NOT_EXISTS\x10\xe1\x03\x12\x1d\n\x18SIP_STATUS_LOOP_DETECTED\x10\xe2\x03\x12\x1d\n\x18SIP_STATUS_TOO_MANY_HOPS\x10\xe3\x03\x12\"\n\x1dSIP_STATUS_ADDRESS_INCOMPLETE\x10\xe4\x03\x12\x19\n\x14SIP_STATUS_AMBIGUOUS\x10\xe5\x03\x12\x19\n\x14SIP_STATUS_BUSY_HERE\x10\xe6\x03\x12\"\n\x1dSIP_STATUS_REQUEST_TERMINATED\x10\xe7\x03\x12#\n\x1eSIP_STATUS_NOT_ACCEPTABLE_HERE\x10\xe8\x03\x12\x19\n\x14SIP_STATUS_BAD_EVENT\x10\xe9\x03\x12\x1f\n\x1aSIP_STATUS_REQUEST_PENDING\x10\xeb\x03\x12\x1e\n\x19SIP_STATUS_UNDECIPHERABLE\x10\xed\x03\x12+\n&SIP_STATUS_SECURITY_AGREEMENT_REQUIRED\x10\xee\x03\x12%\n SIP_STATUS_INTERNAL_SERVER_ERROR\x10\xf4\x03\x12\x1f\n\x1aSIP_STATUS_NOT_IMPLEMENTED\x10\xf5\x03\x12\x1b\n\x16SIP_STATUS_BAD_GATEWAY\x10\xf6\x03\x12#\n\x1eSIP_STATUS_SERVICE_UNAVAILABLE\x10\xf7\x03\x12\x1f\n\x1aSIP_STATUS_GATEWAY_TIMEOUT\x10\xf8\x03\x12%\n SIP_STATUS_VERSION_NOT_SUPPORTED\x10\xf9\x03\x12!\n\x1cSIP_STATUS_MESSAGE_TOO_LARGE\x10\x81\x04\x12&\n!SIP_STATUS_GLOBAL_BUSY_EVERYWHERE\x10\xd8\x04\x12\x1e\n\x19SIP_STATUS_GLOBAL_DECLINE\x10\xdb\x04\x12.\n)SIP_STATUS_GLOBAL_DOES_NOT_EXIST_ANYWHERE\x10\xdc\x04\x12%\n SIP_STATUS_GLOBAL_NOT_ACCEPTABLE\x10\xde\x04\x12\x1f\n\x1aSIP_STATUS_GLOBAL_UNWANTED\x10\xdf\x04\x12\x1f\n\x1aSIP_STATUS_GLOBAL_REJECTED\x10\xe0\x04*k\n\x0cSIPTransport\x12\x16\n\x12SIP_TRANSPORT_AUTO\x10\x00\x12\x15\n\x11SIP_TRANSPORT_UDP\x10\x01\x12\x15\n\x11SIP_TRANSPORT_TCP\x10\x02\x12\x15\n\x11SIP_TRANSPORT_TLS\x10\x03*N\n\x10SIPHeaderOptions\x12\x12\n\x0eSIP_NO_HEADERS\x10\x00\x12\x11\n\rSIP_X_HEADERS\x10\x01\x12\x13\n\x0fSIP_ALL_HEADERS\x10\x02*o\n\x12SIPMediaEncryption\x12\x1d\n\x19SIP_MEDIA_ENCRYPT_DISABLE\x10\x00\x12\x1b\n\x17SIP_MEDIA_ENCRYPT_ALLOW\x10\x01\x12\x1d\n\x19SIP_MEDIA_ENCRYPT_REQUIRE\x10\x02*a\n\x0cProviderType\x12\x19\n\x15PROVIDER_TYPE_UNKNOWN\x10\x00\x12\x1a\n\x16PROVIDER_TYPE_INTERNAL\x10\x01\x12\x1a\n\x16PROVIDER_TYPE_EXTERNAL\x10\x02*w\n\rSIPCallStatus\x12\x15\n\x11SCS_CALL_INCOMING\x10\x00\x12\x1a\n\x16SCS_PARTICIPANT_JOINED\x10\x01\x12\x0e\n\nSCS_ACTIVE\x10\x02\x12\x14\n\x10SCS_DISCONNECTED\x10\x03\x12\r\n\tSCS_ERROR\x10\x04*c\n\x11SIPTransferStatus\x12\x18\n\x14STS_TRANSFER_ONGOING\x10\x00\x12\x17\n\x13STS_TRANSFER_FAILED\x10\x01\x12\x1b\n\x17STS_TRANSFER_SUCCESSFUL\x10\x02*)\n\nSIPFeature\x12\x08\n\x04NONE\x10\x00\x12\x11\n\rKRISP_ENABLED\x10\x01*F\n\x10SIPCallDirection\x12\x0f\n\x0bSCD_UNKNOWN\x10\x00\x12\x0f\n\x0bSCD_INBOUND\x10\x01\x12\x10\n\x0cSCD_OUTBOUND\x10\x02\x32\xd7\x0b\n\x03SIP\x12P\n\x0cListSIPTrunk\x12\x1c.livekit.ListSIPTrunkRequest\x1a\x1d.livekit.ListSIPTrunkResponse\"\x03\x88\x02\x01\x12\\\n\x15\x43reateSIPInboundTrunk\x12%.livekit.CreateSIPInboundTrunkRequest\x1a\x1c.livekit.SIPInboundTrunkInfo\x12_\n\x16\x43reateSIPOutboundTrunk\x12&.livekit.CreateSIPOutboundTrunkRequest\x1a\x1d.livekit.SIPOutboundTrunkInfo\x12\\\n\x15UpdateSIPInboundTrunk\x12%.livekit.UpdateSIPInboundTrunkRequest\x1a\x1c.livekit.SIPInboundTrunkInfo\x12_\n\x16UpdateSIPOutboundTrunk\x12&.livekit.UpdateSIPOutboundTrunkRequest\x1a\x1d.livekit.SIPOutboundTrunkInfo\x12]\n\x12GetSIPInboundTrunk\x12\".livekit.GetSIPInboundTrunkRequest\x1a#.livekit.GetSIPInboundTrunkResponse\x12`\n\x13GetSIPOutboundTrunk\x12#.livekit.GetSIPOutboundTrunkRequest\x1a$.livekit.GetSIPOutboundTrunkResponse\x12`\n\x13ListSIPInboundTrunk\x12#.livekit.ListSIPInboundTrunkRequest\x1a$.livekit.ListSIPInboundTrunkResponse\x12\x63\n\x14ListSIPOutboundTrunk\x12$.livekit.ListSIPOutboundTrunkRequest\x1a%.livekit.ListSIPOutboundTrunkResponse\x12G\n\x0e\x44\x65leteSIPTrunk\x12\x1e.livekit.DeleteSIPTrunkRequest\x1a\x15.livekit.SIPTrunkInfo\x12\\\n\x15\x43reateSIPDispatchRule\x12%.livekit.CreateSIPDispatchRuleRequest\x1a\x1c.livekit.SIPDispatchRuleInfo\x12\\\n\x15UpdateSIPDispatchRule\x12%.livekit.UpdateSIPDispatchRuleRequest\x1a\x1c.livekit.SIPDispatchRuleInfo\x12`\n\x13ListSIPDispatchRule\x12#.livekit.ListSIPDispatchRuleRequest\x1a$.livekit.ListSIPDispatchRuleResponse\x12\\\n\x15\x44\x65leteSIPDispatchRule\x12%.livekit.DeleteSIPDispatchRuleRequest\x1a\x1c.livekit.SIPDispatchRuleInfo\x12Y\n\x14\x43reateSIPParticipant\x12$.livekit.CreateSIPParticipantRequest\x1a\x1b.livekit.SIPParticipantInfo\x12X\n\x16TransferSIPParticipant\x12&.livekit.TransferSIPParticipantRequest\x1a\x16.google.protobuf.EmptyBFZ#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'sip', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' - _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_numbers_regex']._options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_numbers_regex']._loaded_options = None _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_numbers_regex']._serialized_options = b'\030\001' - _globals['_CREATESIPTRUNKREQUEST']._options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_username']._loaded_options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_username']._serialized_options = b'\300P\001' + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_password']._loaded_options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['inbound_password']._serialized_options = b'\300P\002' + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['outbound_username']._loaded_options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['outbound_username']._serialized_options = b'\300P\001' + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['outbound_password']._loaded_options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['outbound_password']._serialized_options = b'\300P\002' + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_CREATESIPTRUNKREQUEST'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATESIPTRUNKREQUEST']._loaded_options = None _globals['_CREATESIPTRUNKREQUEST']._serialized_options = b'\030\001' - _globals['_SIPTRUNKINFO'].fields_by_name['inbound_numbers_regex']._options = None + _globals['_SIPTRUNKINFO'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_SIPTRUNKINFO'].fields_by_name['inbound_numbers_regex']._loaded_options = None _globals['_SIPTRUNKINFO'].fields_by_name['inbound_numbers_regex']._serialized_options = b'\030\001' - _globals['_SIPTRUNKINFO']._options = None + _globals['_SIPTRUNKINFO'].fields_by_name['inbound_username']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['inbound_username']._serialized_options = b'\300P\001' + _globals['_SIPTRUNKINFO'].fields_by_name['inbound_password']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['inbound_password']._serialized_options = b'\300P\002' + _globals['_SIPTRUNKINFO'].fields_by_name['outbound_username']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['outbound_username']._serialized_options = b'\300P\001' + _globals['_SIPTRUNKINFO'].fields_by_name['outbound_password']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['outbound_password']._serialized_options = b'\300P\002' + _globals['_SIPTRUNKINFO'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPTRUNKINFO'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPTRUNKINFO']._loaded_options = None _globals['_SIPTRUNKINFO']._serialized_options = b'\030\001' - _globals['_LISTSIPTRUNKREQUEST']._options = None + _globals['_UPDATESIPINBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_UPDATESIPINBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_SIPINBOUNDTRUNKINFO_HEADERSENTRY']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO_HEADERSENTRY']._serialized_options = b'8\001' + _globals['_SIPINBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIPINBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_options = b'8\001' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['auth_username']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['auth_username']._serialized_options = b'\300P\001' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['auth_password']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['auth_password']._serialized_options = b'\300P\002' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['headers']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['headers']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPINBOUNDTRUNKINFO'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['auth_username']._loaded_options = None + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['auth_username']._serialized_options = b'\300P\001' + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['auth_password']._loaded_options = None + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['auth_password']._serialized_options = b'\300P\002' + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPINBOUNDTRUNKUPDATE'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_UPDATESIPOUTBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_UPDATESIPOUTBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSENTRY']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSENTRY']._serialized_options = b'8\001' + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIPOUTBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_options = b'8\001' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['auth_username']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['auth_username']._serialized_options = b'\300P\001' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['auth_password']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['auth_password']._serialized_options = b'\300P\002' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['headers']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['headers']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKINFO'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['auth_username']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['auth_username']._serialized_options = b'\300P\001' + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['auth_password']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['auth_password']._serialized_options = b'\300P\002' + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPOUTBOUNDTRUNKUPDATE'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_GETSIPINBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_GETSIPINBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_GETSIPOUTBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_GETSIPOUTBOUNDTRUNKREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_LISTSIPTRUNKREQUEST']._loaded_options = None _globals['_LISTSIPTRUNKREQUEST']._serialized_options = b'\030\001' - _globals['_LISTSIPTRUNKRESPONSE']._options = None + _globals['_LISTSIPTRUNKRESPONSE']._loaded_options = None _globals['_LISTSIPTRUNKRESPONSE']._serialized_options = b'\030\001' - _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._options = None + _globals['_DELETESIPTRUNKREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_DELETESIPTRUNKREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._loaded_options = None _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['rule']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['rule']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['trunk_ids']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['trunk_ids']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['hide_phone_number']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['hide_phone_number']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['inbound_numbers']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['inbound_numbers']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['name']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['name']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['metadata']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['metadata']._serialized_options = b'\030\001\262P\036\300P\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['attributes']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['attributes']._serialized_options = b'\030\001\262P\036\300P\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['room_preset']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['room_preset']._serialized_options = b'\030\001' + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['room_config']._loaded_options = None + _globals['_CREATESIPDISPATCHRULEREQUEST'].fields_by_name['room_config']._serialized_options = b'\030\001' + _globals['_UPDATESIPDISPATCHRULEREQUEST'].fields_by_name['sip_dispatch_rule_id']._loaded_options = None + _globals['_UPDATESIPDISPATCHRULEREQUEST'].fields_by_name['sip_dispatch_rule_id']._serialized_options = b'\272P\021sipDispatchRuleID' + _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._loaded_options = None _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._options = None + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['sip_dispatch_rule_id']._loaded_options = None + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['sip_dispatch_rule_id']._serialized_options = b'\272P\021sipDispatchRuleID' + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['attributes']._loaded_options = None + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPDISPATCHRULEINFO'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_SIPDISPATCHRULEUPDATE_ATTRIBUTESENTRY']._loaded_options = None + _globals['_SIPDISPATCHRULEUPDATE_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['metadata']._loaded_options = None + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['attributes']._loaded_options = None + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['media_encryption']._loaded_options = None + _globals['_SIPDISPATCHRULEUPDATE'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_DELETESIPDISPATCHRULEREQUEST'].fields_by_name['sip_dispatch_rule_id']._loaded_options = None + _globals['_DELETESIPDISPATCHRULEREQUEST'].fields_by_name['sip_dispatch_rule_id']._serialized_options = b'\272P\021sipDispatchRuleID' + _globals['_SIPOUTBOUNDCONFIG_HEADERSTOATTRIBUTESENTRY']._loaded_options = None + _globals['_SIPOUTBOUNDCONFIG_HEADERSTOATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIPOUTBOUNDCONFIG_ATTRIBUTESTOHEADERSENTRY']._loaded_options = None + _globals['_SIPOUTBOUNDCONFIG_ATTRIBUTESTOHEADERSENTRY']._serialized_options = b'8\001' + _globals['_SIPOUTBOUNDCONFIG'].fields_by_name['auth_username']._loaded_options = None + _globals['_SIPOUTBOUNDCONFIG'].fields_by_name['auth_username']._serialized_options = b'\300P\001' + _globals['_SIPOUTBOUNDCONFIG'].fields_by_name['auth_password']._loaded_options = None + _globals['_SIPOUTBOUNDCONFIG'].fields_by_name['auth_password']._serialized_options = b'\300P\002' + _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' - _globals['_SIP'].methods_by_name['CreateSIPTrunk']._options = None - _globals['_SIP'].methods_by_name['CreateSIPTrunk']._serialized_options = b'\210\002\001' - _globals['_SIP'].methods_by_name['ListSIPTrunk']._options = None + _globals['_CREATESIPPARTICIPANTREQUEST_HEADERSENTRY']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_options = b'8\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['sip_trunk_id']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['sip_trunk_id']._serialized_options = b'\272P\nsipTrunkID' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_name']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_name']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_metadata']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_metadata']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['play_ringtone']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['play_ringtone']._serialized_options = b'\030\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['headers']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['headers']._serialized_options = b'\262P\036\300P\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['media_encryption']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['media_encryption']._serialized_options = b'\030\001' + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['display_name']._loaded_options = None + _globals['_CREATESIPPARTICIPANTREQUEST'].fields_by_name['display_name']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPPARTICIPANTINFO'].fields_by_name['participant_id']._loaded_options = None + _globals['_SIPPARTICIPANTINFO'].fields_by_name['participant_id']._serialized_options = b'\272P\rparticipantID' + _globals['_SIPPARTICIPANTINFO'].fields_by_name['sip_call_id']._loaded_options = None + _globals['_SIPPARTICIPANTINFO'].fields_by_name['sip_call_id']._serialized_options = b'\272P\tsipCallID' + _globals['_TRANSFERSIPPARTICIPANTREQUEST_HEADERSENTRY']._loaded_options = None + _globals['_TRANSFERSIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_options = b'8\001' + _globals['_TRANSFERSIPPARTICIPANTREQUEST'].fields_by_name['headers']._loaded_options = None + _globals['_TRANSFERSIPPARTICIPANTREQUEST'].fields_by_name['headers']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPCALLINFO_PARTICIPANTATTRIBUTESENTRY']._loaded_options = None + _globals['_SIPCALLINFO_PARTICIPANTATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIPCALLINFO'].fields_by_name['call_id']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_SIPCALLINFO'].fields_by_name['trunk_id']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['trunk_id']._serialized_options = b'\272P\007trunkID' + _globals['_SIPCALLINFO'].fields_by_name['dispatch_rule_id']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['dispatch_rule_id']._serialized_options = b'\272P\016dispatchRuleID' + _globals['_SIPCALLINFO'].fields_by_name['room_id']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['room_id']._serialized_options = b'\272P\006roomID' + _globals['_SIPCALLINFO'].fields_by_name['participant_attributes']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['participant_attributes']._serialized_options = b'\262P\036\300P\001' + _globals['_SIPCALLINFO'].fields_by_name['created_at']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['created_at']._serialized_options = b'\030\001' + _globals['_SIPCALLINFO'].fields_by_name['started_at']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['started_at']._serialized_options = b'\030\001' + _globals['_SIPCALLINFO'].fields_by_name['ended_at']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['ended_at']._serialized_options = b'\030\001' + _globals['_SIPCALLINFO'].fields_by_name['sip_call_id']._loaded_options = None + _globals['_SIPCALLINFO'].fields_by_name['sip_call_id']._serialized_options = b'\272P\tsipCallID' + _globals['_SIPTRANSFERINFO'].fields_by_name['transfer_id']._loaded_options = None + _globals['_SIPTRANSFERINFO'].fields_by_name['transfer_id']._serialized_options = b'\272P\ntransferID' + _globals['_SIPTRANSFERINFO'].fields_by_name['call_id']._loaded_options = None + _globals['_SIPTRANSFERINFO'].fields_by_name['call_id']._serialized_options = b'\272P\006callID' + _globals['_SIP'].methods_by_name['ListSIPTrunk']._loaded_options = None _globals['_SIP'].methods_by_name['ListSIPTrunk']._serialized_options = b'\210\002\001' - _globals['_SIPTRANSPORT']._serialized_start=3306 - _globals['_SIPTRANSPORT']._serialized_end=3413 - _globals['_CREATESIPTRUNKREQUEST']._serialized_start=31 - _globals['_CREATESIPTRUNKREQUEST']._serialized_end=334 - _globals['_SIPTRUNKINFO']._serialized_start=337 - _globals['_SIPTRUNKINFO']._serialized_end=812 - _globals['_SIPTRUNKINFO_TRUNKKIND']._serialized_start=740 - _globals['_SIPTRUNKINFO_TRUNKKIND']._serialized_end=808 - _globals['_CREATESIPINBOUNDTRUNKREQUEST']._serialized_start=814 - _globals['_CREATESIPINBOUNDTRUNKREQUEST']._serialized_end=889 - _globals['_SIPINBOUNDTRUNKINFO']._serialized_start=892 - _globals['_SIPINBOUNDTRUNKINFO']._serialized_end=1082 - _globals['_CREATESIPOUTBOUNDTRUNKREQUEST']._serialized_start=1084 - _globals['_CREATESIPOUTBOUNDTRUNKREQUEST']._serialized_end=1161 - _globals['_SIPOUTBOUNDTRUNKINFO']._serialized_start=1164 - _globals['_SIPOUTBOUNDTRUNKINFO']._serialized_end=1362 - _globals['_LISTSIPTRUNKREQUEST']._serialized_start=1364 - _globals['_LISTSIPTRUNKREQUEST']._serialized_end=1389 - _globals['_LISTSIPTRUNKRESPONSE']._serialized_start=1391 - _globals['_LISTSIPTRUNKRESPONSE']._serialized_end=1455 - _globals['_LISTSIPINBOUNDTRUNKREQUEST']._serialized_start=1457 - _globals['_LISTSIPINBOUNDTRUNKREQUEST']._serialized_end=1485 - _globals['_LISTSIPINBOUNDTRUNKRESPONSE']._serialized_start=1487 - _globals['_LISTSIPINBOUNDTRUNKRESPONSE']._serialized_end=1561 - _globals['_LISTSIPOUTBOUNDTRUNKREQUEST']._serialized_start=1563 - _globals['_LISTSIPOUTBOUNDTRUNKREQUEST']._serialized_end=1592 - _globals['_LISTSIPOUTBOUNDTRUNKRESPONSE']._serialized_start=1594 - _globals['_LISTSIPOUTBOUNDTRUNKRESPONSE']._serialized_end=1670 - _globals['_DELETESIPTRUNKREQUEST']._serialized_start=1672 - _globals['_DELETESIPTRUNKREQUEST']._serialized_end=1717 - _globals['_SIPDISPATCHRULEDIRECT']._serialized_start=1719 - _globals['_SIPDISPATCHRULEDIRECT']._serialized_end=1774 - _globals['_SIPDISPATCHRULEINDIVIDUAL']._serialized_start=1776 - _globals['_SIPDISPATCHRULEINDIVIDUAL']._serialized_end=1837 - _globals['_SIPDISPATCHRULE']._serialized_start=1840 - _globals['_SIPDISPATCHRULE']._serialized_end=2001 - _globals['_CREATESIPDISPATCHRULEREQUEST']._serialized_start=2004 - _globals['_CREATESIPDISPATCHRULEREQUEST']._serialized_end=2303 - _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._serialized_start=2254 - _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._serialized_end=2303 - _globals['_SIPDISPATCHRULEINFO']._serialized_start=2306 - _globals['_SIPDISPATCHRULEINFO']._serialized_end=2617 - _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._serialized_start=2254 - _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._serialized_end=2303 - _globals['_LISTSIPDISPATCHRULEREQUEST']._serialized_start=2619 - _globals['_LISTSIPDISPATCHRULEREQUEST']._serialized_end=2647 - _globals['_LISTSIPDISPATCHRULERESPONSE']._serialized_start=2649 - _globals['_LISTSIPDISPATCHRULERESPONSE']._serialized_end=2723 - _globals['_DELETESIPDISPATCHRULEREQUEST']._serialized_start=2725 - _globals['_DELETESIPDISPATCHRULEREQUEST']._serialized_end=2785 - _globals['_CREATESIPPARTICIPANTREQUEST']._serialized_start=2788 - _globals['_CREATESIPPARTICIPANTREQUEST']._serialized_end=3188 - _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=3128 - _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=3188 - _globals['_SIPPARTICIPANTINFO']._serialized_start=3190 - _globals['_SIPPARTICIPANTINFO']._serialized_end=3304 - _globals['_SIP']._serialized_start=3416 - _globals['_SIP']._serialized_end=4421 + _globals['_SIPSTATUSCODE']._serialized_start=12787 + _globals['_SIPSTATUSCODE']._serialized_end=15441 + _globals['_SIPTRANSPORT']._serialized_start=15443 + _globals['_SIPTRANSPORT']._serialized_end=15550 + _globals['_SIPHEADEROPTIONS']._serialized_start=15552 + _globals['_SIPHEADEROPTIONS']._serialized_end=15630 + _globals['_SIPMEDIAENCRYPTION']._serialized_start=15632 + _globals['_SIPMEDIAENCRYPTION']._serialized_end=15743 + _globals['_PROVIDERTYPE']._serialized_start=15745 + _globals['_PROVIDERTYPE']._serialized_end=15842 + _globals['_SIPCALLSTATUS']._serialized_start=15844 + _globals['_SIPCALLSTATUS']._serialized_end=15963 + _globals['_SIPTRANSFERSTATUS']._serialized_start=15965 + _globals['_SIPTRANSFERSTATUS']._serialized_end=16064 + _globals['_SIPFEATURE']._serialized_start=16066 + _globals['_SIPFEATURE']._serialized_end=16107 + _globals['_SIPCALLDIRECTION']._serialized_start=16109 + _globals['_SIPCALLDIRECTION']._serialized_end=16179 + _globals['_SIPSTATUS']._serialized_start=215 + _globals['_SIPSTATUS']._serialized_end=280 + _globals['_CREATESIPTRUNKREQUEST']._serialized_start=283 + _globals['_CREATESIPTRUNKREQUEST']._serialized_end=644 + _globals['_SIPCODEC']._serialized_start=646 + _globals['_SIPCODEC']._serialized_end=684 + _globals['_SIPMEDIACONFIG']._serialized_start=687 + _globals['_SIPMEDIACONFIG']._serialized_end=908 + _globals['_PROVIDERINFO']._serialized_start=910 + _globals['_PROVIDERINFO']._serialized_end=1013 + _globals['_SIPTRUNKINFO']._serialized_start=1016 + _globals['_SIPTRUNKINFO']._serialized_end=1564 + _globals['_SIPTRUNKINFO_TRUNKKIND']._serialized_start=1492 + _globals['_SIPTRUNKINFO_TRUNKKIND']._serialized_end=1560 + _globals['_CREATESIPINBOUNDTRUNKREQUEST']._serialized_start=1566 + _globals['_CREATESIPINBOUNDTRUNKREQUEST']._serialized_end=1641 + _globals['_UPDATESIPINBOUNDTRUNKREQUEST']._serialized_start=1644 + _globals['_UPDATESIPINBOUNDTRUNKREQUEST']._serialized_end=1820 + _globals['_SIPINBOUNDTRUNKINFO']._serialized_start=1823 + _globals['_SIPINBOUNDTRUNKINFO']._serialized_end=2910 + _globals['_SIPINBOUNDTRUNKINFO_HEADERSENTRY']._serialized_start=2744 + _globals['_SIPINBOUNDTRUNKINFO_HEADERSENTRY']._serialized_end=2790 + _globals['_SIPINBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_start=2792 + _globals['_SIPINBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_end=2850 + _globals['_SIPINBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_start=2852 + _globals['_SIPINBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_end=2910 + _globals['_SIPINBOUNDTRUNKUPDATE']._serialized_start=2913 + _globals['_SIPINBOUNDTRUNKUPDATE']._serialized_end=3437 + _globals['_CREATESIPOUTBOUNDTRUNKREQUEST']._serialized_start=3439 + _globals['_CREATESIPOUTBOUNDTRUNKREQUEST']._serialized_end=3516 + _globals['_UPDATESIPOUTBOUNDTRUNKREQUEST']._serialized_start=3519 + _globals['_UPDATESIPOUTBOUNDTRUNKREQUEST']._serialized_end=3698 + _globals['_SIPOUTBOUNDTRUNKINFO']._serialized_start=3701 + _globals['_SIPOUTBOUNDTRUNKINFO']._serialized_end=4698 + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSENTRY']._serialized_start=2744 + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSENTRY']._serialized_end=2790 + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_start=2792 + _globals['_SIPOUTBOUNDTRUNKINFO_HEADERSTOATTRIBUTESENTRY']._serialized_end=2850 + _globals['_SIPOUTBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_start=2852 + _globals['_SIPOUTBOUNDTRUNKINFO_ATTRIBUTESTOHEADERSENTRY']._serialized_end=2910 + _globals['_SIPOUTBOUNDTRUNKUPDATE']._serialized_start=4701 + _globals['_SIPOUTBOUNDTRUNKUPDATE']._serialized_end=5283 + _globals['_GETSIPINBOUNDTRUNKREQUEST']._serialized_start=5285 + _globals['_GETSIPINBOUNDTRUNKREQUEST']._serialized_end=5349 + _globals['_GETSIPINBOUNDTRUNKRESPONSE']._serialized_start=5351 + _globals['_GETSIPINBOUNDTRUNKRESPONSE']._serialized_end=5424 + _globals['_GETSIPOUTBOUNDTRUNKREQUEST']._serialized_start=5426 + _globals['_GETSIPOUTBOUNDTRUNKREQUEST']._serialized_end=5491 + _globals['_GETSIPOUTBOUNDTRUNKRESPONSE']._serialized_start=5493 + _globals['_GETSIPOUTBOUNDTRUNKRESPONSE']._serialized_end=5568 + _globals['_LISTSIPTRUNKREQUEST']._serialized_start=5570 + _globals['_LISTSIPTRUNKREQUEST']._serialized_end=5630 + _globals['_LISTSIPTRUNKRESPONSE']._serialized_start=5632 + _globals['_LISTSIPTRUNKRESPONSE']._serialized_end=5696 + _globals['_LISTSIPINBOUNDTRUNKREQUEST']._serialized_start=5698 + _globals['_LISTSIPINBOUNDTRUNKREQUEST']._serialized_end=5797 + _globals['_LISTSIPINBOUNDTRUNKRESPONSE']._serialized_start=5799 + _globals['_LISTSIPINBOUNDTRUNKRESPONSE']._serialized_end=5873 + _globals['_LISTSIPOUTBOUNDTRUNKREQUEST']._serialized_start=5875 + _globals['_LISTSIPOUTBOUNDTRUNKREQUEST']._serialized_end=5975 + _globals['_LISTSIPOUTBOUNDTRUNKRESPONSE']._serialized_start=5977 + _globals['_LISTSIPOUTBOUNDTRUNKRESPONSE']._serialized_end=6053 + _globals['_DELETESIPTRUNKREQUEST']._serialized_start=6055 + _globals['_DELETESIPTRUNKREQUEST']._serialized_end=6115 + _globals['_SIPDISPATCHRULEDIRECT']._serialized_start=6117 + _globals['_SIPDISPATCHRULEDIRECT']._serialized_end=6172 + _globals['_SIPDISPATCHRULEINDIVIDUAL']._serialized_start=6174 + _globals['_SIPDISPATCHRULEINDIVIDUAL']._serialized_end=6258 + _globals['_SIPDISPATCHRULECALLEE']._serialized_start=6260 + _globals['_SIPDISPATCHRULECALLEE']._serialized_end=6336 + _globals['_SIPDISPATCHRULE']._serialized_start=6339 + _globals['_SIPDISPATCHRULE']._serialized_end=6564 + _globals['_CREATESIPDISPATCHRULEREQUEST']._serialized_start=6567 + _globals['_CREATESIPDISPATCHRULEREQUEST']._serialized_end=7097 + _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._serialized_start=7048 + _globals['_CREATESIPDISPATCHRULEREQUEST_ATTRIBUTESENTRY']._serialized_end=7097 + _globals['_UPDATESIPDISPATCHRULEREQUEST']._serialized_start=7100 + _globals['_UPDATESIPDISPATCHRULEREQUEST']._serialized_end=7291 + _globals['_SIPDISPATCHRULEINFO']._serialized_start=7294 + _globals['_SIPDISPATCHRULEINFO']._serialized_end=8008 + _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._serialized_start=7048 + _globals['_SIPDISPATCHRULEINFO_ATTRIBUTESENTRY']._serialized_end=7097 + _globals['_SIPDISPATCHRULEUPDATE']._serialized_start=8011 + _globals['_SIPDISPATCHRULEUPDATE']._serialized_end=8498 + _globals['_SIPDISPATCHRULEUPDATE_ATTRIBUTESENTRY']._serialized_start=7048 + _globals['_SIPDISPATCHRULEUPDATE_ATTRIBUTESENTRY']._serialized_end=7097 + _globals['_LISTSIPDISPATCHRULEREQUEST']._serialized_start=8500 + _globals['_LISTSIPDISPATCHRULEREQUEST']._serialized_end=8609 + _globals['_LISTSIPDISPATCHRULERESPONSE']._serialized_start=8611 + _globals['_LISTSIPDISPATCHRULERESPONSE']._serialized_end=8685 + _globals['_DELETESIPDISPATCHRULEREQUEST']._serialized_start=8687 + _globals['_DELETESIPDISPATCHRULEREQUEST']._serialized_end=8769 + _globals['_SIPOUTBOUNDCONFIG']._serialized_start=8772 + _globals['_SIPOUTBOUNDCONFIG']._serialized_end=9243 + _globals['_SIPOUTBOUNDCONFIG_HEADERSTOATTRIBUTESENTRY']._serialized_start=2792 + _globals['_SIPOUTBOUNDCONFIG_HEADERSTOATTRIBUTESENTRY']._serialized_end=2850 + _globals['_SIPOUTBOUNDCONFIG_ATTRIBUTESTOHEADERSENTRY']._serialized_start=2852 + _globals['_SIPOUTBOUNDCONFIG_ATTRIBUTESTOHEADERSENTRY']._serialized_end=2910 + _globals['_CREATESIPPARTICIPANTREQUEST']._serialized_start=9246 + _globals['_CREATESIPPARTICIPANTREQUEST']._serialized_end=10619 + _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_start=10478 + _globals['_CREATESIPPARTICIPANTREQUEST_PARTICIPANTATTRIBUTESENTRY']._serialized_end=10538 + _globals['_CREATESIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_start=2744 + _globals['_CREATESIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_end=2790 + _globals['_SIPPARTICIPANTINFO']._serialized_start=10622 + _globals['_SIPPARTICIPANTINFO']._serialized_end=10768 + _globals['_TRANSFERSIPPARTICIPANTREQUEST']._serialized_start=10771 + _globals['_TRANSFERSIPPARTICIPANTREQUEST']._serialized_end=11103 + _globals['_TRANSFERSIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_start=2744 + _globals['_TRANSFERSIPPARTICIPANTREQUEST_HEADERSENTRY']._serialized_end=2790 + _globals['_SIPCALLINFO']._serialized_start=11106 + _globals['_SIPCALLINFO']._serialized_end=12155 + _globals['_SIPCALLINFO_PARTICIPANTATTRIBUTESENTRY']._serialized_start=10478 + _globals['_SIPCALLINFO_PARTICIPANTATTRIBUTESENTRY']._serialized_end=10538 + _globals['_SIPTRANSFERINFO']._serialized_start=12158 + _globals['_SIPTRANSFERINFO']._serialized_end=12446 + _globals['_SIPURI']._serialized_start=12448 + _globals['_SIPURI']._serialized_end=12552 + _globals['_SIPREQUESTDEST']._serialized_start=12554 + _globals['_SIPREQUESTDEST']._serialized_end=12627 + _globals['_SIPNAMEDDEST']._serialized_start=12629 + _globals['_SIPNAMEDDEST']._serialized_end=12722 + _globals['_DESTINATION']._serialized_start=12724 + _globals['_DESTINATION']._serialized_end=12784 + _globals['_SIP']._serialized_start=16182 + _globals['_SIP']._serialized_end=17677 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/livekit/protocol/sip.pyi b/livekit-protocol/livekit/protocol/sip.pyi index 770b8e30..eb75198f 100644 --- a/livekit-protocol/livekit/protocol/sip.pyi +++ b/livekit-protocol/livekit/protocol/sip.pyi @@ -1,3 +1,10 @@ +from google.protobuf import any_pb2 as _any_pb2 +from google.protobuf import duration_pb2 as _duration_pb2 +from google.protobuf import empty_pb2 as _empty_pb2 +from google.protobuf import timestamp_pb2 as _timestamp_pb2 +from . import models as _models +from . import room as _room +from .logger_pb import options as _options_pb2 from google.protobuf.internal import containers as _containers from google.protobuf.internal import enum_type_wrapper as _enum_type_wrapper from google.protobuf import descriptor as _descriptor @@ -6,16 +13,244 @@ from typing import ClassVar as _ClassVar, Iterable as _Iterable, Mapping as _Map DESCRIPTOR: _descriptor.FileDescriptor +class SIPStatusCode(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SIP_STATUS_UNKNOWN: _ClassVar[SIPStatusCode] + SIP_STATUS_TRYING: _ClassVar[SIPStatusCode] + SIP_STATUS_RINGING: _ClassVar[SIPStatusCode] + SIP_STATUS_CALL_IS_FORWARDED: _ClassVar[SIPStatusCode] + SIP_STATUS_QUEUED: _ClassVar[SIPStatusCode] + SIP_STATUS_SESSION_PROGRESS: _ClassVar[SIPStatusCode] + SIP_STATUS_EARLY_DIALOG_TERMINATED: _ClassVar[SIPStatusCode] + SIP_STATUS_OK: _ClassVar[SIPStatusCode] + SIP_STATUS_ACCEPTED: _ClassVar[SIPStatusCode] + SIP_STATUS_NO_NOTIFICATION: _ClassVar[SIPStatusCode] + SIP_STATUS_MULTIPLE_CHOICES: _ClassVar[SIPStatusCode] + SIP_STATUS_MOVED_PERMANENTLY: _ClassVar[SIPStatusCode] + SIP_STATUS_MOVED_TEMPORARILY: _ClassVar[SIPStatusCode] + SIP_STATUS_USE_PROXY: _ClassVar[SIPStatusCode] + SIP_STATUS_ALTERNATIVE_SERVICE: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_REQUEST: _ClassVar[SIPStatusCode] + SIP_STATUS_UNAUTHORIZED: _ClassVar[SIPStatusCode] + SIP_STATUS_PAYMENT_REQUIRED: _ClassVar[SIPStatusCode] + SIP_STATUS_FORBIDDEN: _ClassVar[SIPStatusCode] + SIP_STATUS_NOTFOUND: _ClassVar[SIPStatusCode] + SIP_STATUS_METHOD_NOT_ALLOWED: _ClassVar[SIPStatusCode] + SIP_STATUS_NOT_ACCEPTABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_PROXY_AUTH_REQUIRED: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUEST_TIMEOUT: _ClassVar[SIPStatusCode] + SIP_STATUS_CONFLICT: _ClassVar[SIPStatusCode] + SIP_STATUS_GONE: _ClassVar[SIPStatusCode] + SIP_STATUS_LENGTH_REQUIRED: _ClassVar[SIPStatusCode] + SIP_STATUS_CONDITIONAL_REQUEST_FAILED: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUEST_ENTITY_TOO_LARGE: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUEST_URI_TOO_LONG: _ClassVar[SIPStatusCode] + SIP_STATUS_UNSUPPORTED_MEDIA_TYPE: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_UNKNOWN_RESOURCE_PRIORITY: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_EXTENSION: _ClassVar[SIPStatusCode] + SIP_STATUS_EXTENSION_REQUIRED: _ClassVar[SIPStatusCode] + SIP_STATUS_SESSION_INTERVAL_TOO_SMALL: _ClassVar[SIPStatusCode] + SIP_STATUS_INTERVAL_TOO_BRIEF: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_LOCATION_INFORMATION: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_ALERT_MESSAGE: _ClassVar[SIPStatusCode] + SIP_STATUS_USE_IDENTITY_HEADER: _ClassVar[SIPStatusCode] + SIP_STATUS_PROVIDE_REFERRER_IDENTITY: _ClassVar[SIPStatusCode] + SIP_STATUS_FLOW_FAILED: _ClassVar[SIPStatusCode] + SIP_STATUS_ANONYMITY_DISALLOWED: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_IDENTITY_INFO: _ClassVar[SIPStatusCode] + SIP_STATUS_UNSUPPORTED_CERTIFICATE: _ClassVar[SIPStatusCode] + SIP_STATUS_INVALID_IDENTITY_HEADER: _ClassVar[SIPStatusCode] + SIP_STATUS_FIRST_HOP_LACKS_OUTBOUND_SUPPORT: _ClassVar[SIPStatusCode] + SIP_STATUS_MAX_BREADTH_EXCEEDED: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_INFO_PACKAGE: _ClassVar[SIPStatusCode] + SIP_STATUS_CONSENT_NEEDED: _ClassVar[SIPStatusCode] + SIP_STATUS_TEMPORARILY_UNAVAILABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_CALL_TRANSACTION_DOES_NOT_EXISTS: _ClassVar[SIPStatusCode] + SIP_STATUS_LOOP_DETECTED: _ClassVar[SIPStatusCode] + SIP_STATUS_TOO_MANY_HOPS: _ClassVar[SIPStatusCode] + SIP_STATUS_ADDRESS_INCOMPLETE: _ClassVar[SIPStatusCode] + SIP_STATUS_AMBIGUOUS: _ClassVar[SIPStatusCode] + SIP_STATUS_BUSY_HERE: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUEST_TERMINATED: _ClassVar[SIPStatusCode] + SIP_STATUS_NOT_ACCEPTABLE_HERE: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_EVENT: _ClassVar[SIPStatusCode] + SIP_STATUS_REQUEST_PENDING: _ClassVar[SIPStatusCode] + SIP_STATUS_UNDECIPHERABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_SECURITY_AGREEMENT_REQUIRED: _ClassVar[SIPStatusCode] + SIP_STATUS_INTERNAL_SERVER_ERROR: _ClassVar[SIPStatusCode] + SIP_STATUS_NOT_IMPLEMENTED: _ClassVar[SIPStatusCode] + SIP_STATUS_BAD_GATEWAY: _ClassVar[SIPStatusCode] + SIP_STATUS_SERVICE_UNAVAILABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_GATEWAY_TIMEOUT: _ClassVar[SIPStatusCode] + SIP_STATUS_VERSION_NOT_SUPPORTED: _ClassVar[SIPStatusCode] + SIP_STATUS_MESSAGE_TOO_LARGE: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_BUSY_EVERYWHERE: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_DECLINE: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_DOES_NOT_EXIST_ANYWHERE: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_NOT_ACCEPTABLE: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_UNWANTED: _ClassVar[SIPStatusCode] + SIP_STATUS_GLOBAL_REJECTED: _ClassVar[SIPStatusCode] + class SIPTransport(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): __slots__ = () SIP_TRANSPORT_AUTO: _ClassVar[SIPTransport] SIP_TRANSPORT_UDP: _ClassVar[SIPTransport] SIP_TRANSPORT_TCP: _ClassVar[SIPTransport] SIP_TRANSPORT_TLS: _ClassVar[SIPTransport] + +class SIPHeaderOptions(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SIP_NO_HEADERS: _ClassVar[SIPHeaderOptions] + SIP_X_HEADERS: _ClassVar[SIPHeaderOptions] + SIP_ALL_HEADERS: _ClassVar[SIPHeaderOptions] + +class SIPMediaEncryption(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SIP_MEDIA_ENCRYPT_DISABLE: _ClassVar[SIPMediaEncryption] + SIP_MEDIA_ENCRYPT_ALLOW: _ClassVar[SIPMediaEncryption] + SIP_MEDIA_ENCRYPT_REQUIRE: _ClassVar[SIPMediaEncryption] + +class ProviderType(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + PROVIDER_TYPE_UNKNOWN: _ClassVar[ProviderType] + PROVIDER_TYPE_INTERNAL: _ClassVar[ProviderType] + PROVIDER_TYPE_EXTERNAL: _ClassVar[ProviderType] + +class SIPCallStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SCS_CALL_INCOMING: _ClassVar[SIPCallStatus] + SCS_PARTICIPANT_JOINED: _ClassVar[SIPCallStatus] + SCS_ACTIVE: _ClassVar[SIPCallStatus] + SCS_DISCONNECTED: _ClassVar[SIPCallStatus] + SCS_ERROR: _ClassVar[SIPCallStatus] + +class SIPTransferStatus(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + STS_TRANSFER_ONGOING: _ClassVar[SIPTransferStatus] + STS_TRANSFER_FAILED: _ClassVar[SIPTransferStatus] + STS_TRANSFER_SUCCESSFUL: _ClassVar[SIPTransferStatus] + +class SIPFeature(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + NONE: _ClassVar[SIPFeature] + KRISP_ENABLED: _ClassVar[SIPFeature] + +class SIPCallDirection(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): + __slots__ = () + SCD_UNKNOWN: _ClassVar[SIPCallDirection] + SCD_INBOUND: _ClassVar[SIPCallDirection] + SCD_OUTBOUND: _ClassVar[SIPCallDirection] +SIP_STATUS_UNKNOWN: SIPStatusCode +SIP_STATUS_TRYING: SIPStatusCode +SIP_STATUS_RINGING: SIPStatusCode +SIP_STATUS_CALL_IS_FORWARDED: SIPStatusCode +SIP_STATUS_QUEUED: SIPStatusCode +SIP_STATUS_SESSION_PROGRESS: SIPStatusCode +SIP_STATUS_EARLY_DIALOG_TERMINATED: SIPStatusCode +SIP_STATUS_OK: SIPStatusCode +SIP_STATUS_ACCEPTED: SIPStatusCode +SIP_STATUS_NO_NOTIFICATION: SIPStatusCode +SIP_STATUS_MULTIPLE_CHOICES: SIPStatusCode +SIP_STATUS_MOVED_PERMANENTLY: SIPStatusCode +SIP_STATUS_MOVED_TEMPORARILY: SIPStatusCode +SIP_STATUS_USE_PROXY: SIPStatusCode +SIP_STATUS_ALTERNATIVE_SERVICE: SIPStatusCode +SIP_STATUS_BAD_REQUEST: SIPStatusCode +SIP_STATUS_UNAUTHORIZED: SIPStatusCode +SIP_STATUS_PAYMENT_REQUIRED: SIPStatusCode +SIP_STATUS_FORBIDDEN: SIPStatusCode +SIP_STATUS_NOTFOUND: SIPStatusCode +SIP_STATUS_METHOD_NOT_ALLOWED: SIPStatusCode +SIP_STATUS_NOT_ACCEPTABLE: SIPStatusCode +SIP_STATUS_PROXY_AUTH_REQUIRED: SIPStatusCode +SIP_STATUS_REQUEST_TIMEOUT: SIPStatusCode +SIP_STATUS_CONFLICT: SIPStatusCode +SIP_STATUS_GONE: SIPStatusCode +SIP_STATUS_LENGTH_REQUIRED: SIPStatusCode +SIP_STATUS_CONDITIONAL_REQUEST_FAILED: SIPStatusCode +SIP_STATUS_REQUEST_ENTITY_TOO_LARGE: SIPStatusCode +SIP_STATUS_REQUEST_URI_TOO_LONG: SIPStatusCode +SIP_STATUS_UNSUPPORTED_MEDIA_TYPE: SIPStatusCode +SIP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE: SIPStatusCode +SIP_STATUS_UNKNOWN_RESOURCE_PRIORITY: SIPStatusCode +SIP_STATUS_BAD_EXTENSION: SIPStatusCode +SIP_STATUS_EXTENSION_REQUIRED: SIPStatusCode +SIP_STATUS_SESSION_INTERVAL_TOO_SMALL: SIPStatusCode +SIP_STATUS_INTERVAL_TOO_BRIEF: SIPStatusCode +SIP_STATUS_BAD_LOCATION_INFORMATION: SIPStatusCode +SIP_STATUS_BAD_ALERT_MESSAGE: SIPStatusCode +SIP_STATUS_USE_IDENTITY_HEADER: SIPStatusCode +SIP_STATUS_PROVIDE_REFERRER_IDENTITY: SIPStatusCode +SIP_STATUS_FLOW_FAILED: SIPStatusCode +SIP_STATUS_ANONYMITY_DISALLOWED: SIPStatusCode +SIP_STATUS_BAD_IDENTITY_INFO: SIPStatusCode +SIP_STATUS_UNSUPPORTED_CERTIFICATE: SIPStatusCode +SIP_STATUS_INVALID_IDENTITY_HEADER: SIPStatusCode +SIP_STATUS_FIRST_HOP_LACKS_OUTBOUND_SUPPORT: SIPStatusCode +SIP_STATUS_MAX_BREADTH_EXCEEDED: SIPStatusCode +SIP_STATUS_BAD_INFO_PACKAGE: SIPStatusCode +SIP_STATUS_CONSENT_NEEDED: SIPStatusCode +SIP_STATUS_TEMPORARILY_UNAVAILABLE: SIPStatusCode +SIP_STATUS_CALL_TRANSACTION_DOES_NOT_EXISTS: SIPStatusCode +SIP_STATUS_LOOP_DETECTED: SIPStatusCode +SIP_STATUS_TOO_MANY_HOPS: SIPStatusCode +SIP_STATUS_ADDRESS_INCOMPLETE: SIPStatusCode +SIP_STATUS_AMBIGUOUS: SIPStatusCode +SIP_STATUS_BUSY_HERE: SIPStatusCode +SIP_STATUS_REQUEST_TERMINATED: SIPStatusCode +SIP_STATUS_NOT_ACCEPTABLE_HERE: SIPStatusCode +SIP_STATUS_BAD_EVENT: SIPStatusCode +SIP_STATUS_REQUEST_PENDING: SIPStatusCode +SIP_STATUS_UNDECIPHERABLE: SIPStatusCode +SIP_STATUS_SECURITY_AGREEMENT_REQUIRED: SIPStatusCode +SIP_STATUS_INTERNAL_SERVER_ERROR: SIPStatusCode +SIP_STATUS_NOT_IMPLEMENTED: SIPStatusCode +SIP_STATUS_BAD_GATEWAY: SIPStatusCode +SIP_STATUS_SERVICE_UNAVAILABLE: SIPStatusCode +SIP_STATUS_GATEWAY_TIMEOUT: SIPStatusCode +SIP_STATUS_VERSION_NOT_SUPPORTED: SIPStatusCode +SIP_STATUS_MESSAGE_TOO_LARGE: SIPStatusCode +SIP_STATUS_GLOBAL_BUSY_EVERYWHERE: SIPStatusCode +SIP_STATUS_GLOBAL_DECLINE: SIPStatusCode +SIP_STATUS_GLOBAL_DOES_NOT_EXIST_ANYWHERE: SIPStatusCode +SIP_STATUS_GLOBAL_NOT_ACCEPTABLE: SIPStatusCode +SIP_STATUS_GLOBAL_UNWANTED: SIPStatusCode +SIP_STATUS_GLOBAL_REJECTED: SIPStatusCode SIP_TRANSPORT_AUTO: SIPTransport SIP_TRANSPORT_UDP: SIPTransport SIP_TRANSPORT_TCP: SIPTransport SIP_TRANSPORT_TLS: SIPTransport +SIP_NO_HEADERS: SIPHeaderOptions +SIP_X_HEADERS: SIPHeaderOptions +SIP_ALL_HEADERS: SIPHeaderOptions +SIP_MEDIA_ENCRYPT_DISABLE: SIPMediaEncryption +SIP_MEDIA_ENCRYPT_ALLOW: SIPMediaEncryption +SIP_MEDIA_ENCRYPT_REQUIRE: SIPMediaEncryption +PROVIDER_TYPE_UNKNOWN: ProviderType +PROVIDER_TYPE_INTERNAL: ProviderType +PROVIDER_TYPE_EXTERNAL: ProviderType +SCS_CALL_INCOMING: SIPCallStatus +SCS_PARTICIPANT_JOINED: SIPCallStatus +SCS_ACTIVE: SIPCallStatus +SCS_DISCONNECTED: SIPCallStatus +SCS_ERROR: SIPCallStatus +STS_TRANSFER_ONGOING: SIPTransferStatus +STS_TRANSFER_FAILED: SIPTransferStatus +STS_TRANSFER_SUCCESSFUL: SIPTransferStatus +NONE: SIPFeature +KRISP_ENABLED: SIPFeature +SCD_UNKNOWN: SIPCallDirection +SCD_INBOUND: SIPCallDirection +SCD_OUTBOUND: SIPCallDirection + +class SIPStatus(_message.Message): + __slots__ = ("code", "status") + CODE_FIELD_NUMBER: _ClassVar[int] + STATUS_FIELD_NUMBER: _ClassVar[int] + code: SIPStatusCode + status: str + def __init__(self, code: _Optional[_Union[SIPStatusCode, str]] = ..., status: _Optional[str] = ...) -> None: ... class CreateSIPTrunkRequest(_message.Message): __slots__ = ("inbound_addresses", "outbound_address", "outbound_number", "inbound_numbers_regex", "inbound_numbers", "inbound_username", "inbound_password", "outbound_username", "outbound_password", "name", "metadata") @@ -43,6 +278,38 @@ class CreateSIPTrunkRequest(_message.Message): metadata: str def __init__(self, inbound_addresses: _Optional[_Iterable[str]] = ..., outbound_address: _Optional[str] = ..., outbound_number: _Optional[str] = ..., inbound_numbers_regex: _Optional[_Iterable[str]] = ..., inbound_numbers: _Optional[_Iterable[str]] = ..., inbound_username: _Optional[str] = ..., inbound_password: _Optional[str] = ..., outbound_username: _Optional[str] = ..., outbound_password: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ...) -> None: ... +class SIPCodec(_message.Message): + __slots__ = ("name", "rate") + NAME_FIELD_NUMBER: _ClassVar[int] + RATE_FIELD_NUMBER: _ClassVar[int] + name: str + rate: int + def __init__(self, name: _Optional[str] = ..., rate: _Optional[int] = ...) -> None: ... + +class SIPMediaConfig(_message.Message): + __slots__ = ("only_listed_codecs", "codecs", "encryption", "media_timeout") + ONLY_LISTED_CODECS_FIELD_NUMBER: _ClassVar[int] + CODECS_FIELD_NUMBER: _ClassVar[int] + ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + only_listed_codecs: bool + codecs: _containers.RepeatedCompositeFieldContainer[SIPCodec] + encryption: SIPMediaEncryption + media_timeout: _duration_pb2.Duration + def __init__(self, only_listed_codecs: bool = ..., codecs: _Optional[_Iterable[_Union[SIPCodec, _Mapping]]] = ..., encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class ProviderInfo(_message.Message): + __slots__ = ("id", "name", "type", "prevent_transfer") + ID_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + TYPE_FIELD_NUMBER: _ClassVar[int] + PREVENT_TRANSFER_FIELD_NUMBER: _ClassVar[int] + id: str + name: str + type: ProviderType + prevent_transfer: bool + def __init__(self, id: _Optional[str] = ..., name: _Optional[str] = ..., type: _Optional[_Union[ProviderType, str]] = ..., prevent_transfer: bool = ...) -> None: ... + class SIPTrunkInfo(_message.Message): __slots__ = ("sip_trunk_id", "kind", "inbound_addresses", "outbound_address", "outbound_number", "transport", "inbound_numbers_regex", "inbound_numbers", "inbound_username", "inbound_password", "outbound_username", "outbound_password", "name", "metadata") class TrunkKind(int, metaclass=_enum_type_wrapper.EnumTypeWrapper): @@ -89,8 +356,39 @@ class CreateSIPInboundTrunkRequest(_message.Message): trunk: SIPInboundTrunkInfo def __init__(self, trunk: _Optional[_Union[SIPInboundTrunkInfo, _Mapping]] = ...) -> None: ... +class UpdateSIPInboundTrunkRequest(_message.Message): + __slots__ = ("sip_trunk_id", "replace", "update") + SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + REPLACE_FIELD_NUMBER: _ClassVar[int] + UPDATE_FIELD_NUMBER: _ClassVar[int] + sip_trunk_id: str + replace: SIPInboundTrunkInfo + update: SIPInboundTrunkUpdate + def __init__(self, sip_trunk_id: _Optional[str] = ..., replace: _Optional[_Union[SIPInboundTrunkInfo, _Mapping]] = ..., update: _Optional[_Union[SIPInboundTrunkUpdate, _Mapping]] = ...) -> None: ... + class SIPInboundTrunkInfo(_message.Message): - __slots__ = ("sip_trunk_id", "name", "metadata", "numbers", "allowed_addresses", "allowed_numbers", "auth_username", "auth_password") + __slots__ = ("sip_trunk_id", "name", "metadata", "numbers", "allowed_addresses", "allowed_numbers", "auth_username", "auth_password", "auth_realm", "headers", "headers_to_attributes", "attributes_to_headers", "include_headers", "ringing_timeout", "max_call_duration", "krisp_enabled", "media_encryption", "media", "created_at", "updated_at") + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class HeadersToAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class AttributesToHeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] @@ -99,6 +397,18 @@ class SIPInboundTrunkInfo(_message.Message): ALLOWED_NUMBERS_FIELD_NUMBER: _ClassVar[int] AUTH_USERNAME_FIELD_NUMBER: _ClassVar[int] AUTH_PASSWORD_FIELD_NUMBER: _ClassVar[int] + AUTH_REALM_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + HEADERS_TO_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_TO_HEADERS_FIELD_NUMBER: _ClassVar[int] + INCLUDE_HEADERS_FIELD_NUMBER: _ClassVar[int] + RINGING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + MAX_CALL_DURATION_FIELD_NUMBER: _ClassVar[int] + KRISP_ENABLED_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] sip_trunk_id: str name: str metadata: str @@ -107,7 +417,43 @@ class SIPInboundTrunkInfo(_message.Message): allowed_numbers: _containers.RepeatedScalarFieldContainer[str] auth_username: str auth_password: str - def __init__(self, sip_trunk_id: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., numbers: _Optional[_Iterable[str]] = ..., allowed_addresses: _Optional[_Iterable[str]] = ..., allowed_numbers: _Optional[_Iterable[str]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ...) -> None: ... + auth_realm: str + headers: _containers.ScalarMap[str, str] + headers_to_attributes: _containers.ScalarMap[str, str] + attributes_to_headers: _containers.ScalarMap[str, str] + include_headers: SIPHeaderOptions + ringing_timeout: _duration_pb2.Duration + max_call_duration: _duration_pb2.Duration + krisp_enabled: bool + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + created_at: _timestamp_pb2.Timestamp + updated_at: _timestamp_pb2.Timestamp + def __init__(self, sip_trunk_id: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., numbers: _Optional[_Iterable[str]] = ..., allowed_addresses: _Optional[_Iterable[str]] = ..., allowed_numbers: _Optional[_Iterable[str]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ..., auth_realm: _Optional[str] = ..., headers: _Optional[_Mapping[str, str]] = ..., headers_to_attributes: _Optional[_Mapping[str, str]] = ..., attributes_to_headers: _Optional[_Mapping[str, str]] = ..., include_headers: _Optional[_Union[SIPHeaderOptions, str]] = ..., ringing_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., max_call_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., krisp_enabled: bool = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class SIPInboundTrunkUpdate(_message.Message): + __slots__ = ("numbers", "allowed_addresses", "allowed_numbers", "auth_username", "auth_password", "auth_realm", "name", "metadata", "media_encryption", "media") + NUMBERS_FIELD_NUMBER: _ClassVar[int] + ALLOWED_ADDRESSES_FIELD_NUMBER: _ClassVar[int] + ALLOWED_NUMBERS_FIELD_NUMBER: _ClassVar[int] + AUTH_USERNAME_FIELD_NUMBER: _ClassVar[int] + AUTH_PASSWORD_FIELD_NUMBER: _ClassVar[int] + AUTH_REALM_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + numbers: _models.ListUpdate + allowed_addresses: _models.ListUpdate + allowed_numbers: _models.ListUpdate + auth_username: str + auth_password: str + auth_realm: str + name: str + metadata: str + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + def __init__(self, numbers: _Optional[_Union[_models.ListUpdate, _Mapping]] = ..., allowed_addresses: _Optional[_Union[_models.ListUpdate, _Mapping]] = ..., allowed_numbers: _Optional[_Union[_models.ListUpdate, _Mapping]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ..., auth_realm: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ...) -> None: ... class CreateSIPOutboundTrunkRequest(_message.Message): __slots__ = ("trunk",) @@ -115,29 +461,132 @@ class CreateSIPOutboundTrunkRequest(_message.Message): trunk: SIPOutboundTrunkInfo def __init__(self, trunk: _Optional[_Union[SIPOutboundTrunkInfo, _Mapping]] = ...) -> None: ... +class UpdateSIPOutboundTrunkRequest(_message.Message): + __slots__ = ("sip_trunk_id", "replace", "update") + SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + REPLACE_FIELD_NUMBER: _ClassVar[int] + UPDATE_FIELD_NUMBER: _ClassVar[int] + sip_trunk_id: str + replace: SIPOutboundTrunkInfo + update: SIPOutboundTrunkUpdate + def __init__(self, sip_trunk_id: _Optional[str] = ..., replace: _Optional[_Union[SIPOutboundTrunkInfo, _Mapping]] = ..., update: _Optional[_Union[SIPOutboundTrunkUpdate, _Mapping]] = ...) -> None: ... + class SIPOutboundTrunkInfo(_message.Message): - __slots__ = ("sip_trunk_id", "name", "metadata", "address", "transport", "numbers", "auth_username", "auth_password") + __slots__ = ("sip_trunk_id", "name", "metadata", "address", "destination_country", "transport", "numbers", "auth_username", "auth_password", "headers", "headers_to_attributes", "attributes_to_headers", "include_headers", "media_encryption", "media", "from_host", "created_at", "updated_at") + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class HeadersToAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class AttributesToHeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] ADDRESS_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] TRANSPORT_FIELD_NUMBER: _ClassVar[int] NUMBERS_FIELD_NUMBER: _ClassVar[int] AUTH_USERNAME_FIELD_NUMBER: _ClassVar[int] AUTH_PASSWORD_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + HEADERS_TO_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_TO_HEADERS_FIELD_NUMBER: _ClassVar[int] + INCLUDE_HEADERS_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + FROM_HOST_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] sip_trunk_id: str name: str metadata: str address: str + destination_country: str transport: SIPTransport numbers: _containers.RepeatedScalarFieldContainer[str] auth_username: str auth_password: str - def __init__(self, sip_trunk_id: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., address: _Optional[str] = ..., transport: _Optional[_Union[SIPTransport, str]] = ..., numbers: _Optional[_Iterable[str]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ...) -> None: ... + headers: _containers.ScalarMap[str, str] + headers_to_attributes: _containers.ScalarMap[str, str] + attributes_to_headers: _containers.ScalarMap[str, str] + include_headers: SIPHeaderOptions + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + from_host: str + created_at: _timestamp_pb2.Timestamp + updated_at: _timestamp_pb2.Timestamp + def __init__(self, sip_trunk_id: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., address: _Optional[str] = ..., destination_country: _Optional[str] = ..., transport: _Optional[_Union[SIPTransport, str]] = ..., numbers: _Optional[_Iterable[str]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ..., headers: _Optional[_Mapping[str, str]] = ..., headers_to_attributes: _Optional[_Mapping[str, str]] = ..., attributes_to_headers: _Optional[_Mapping[str, str]] = ..., include_headers: _Optional[_Union[SIPHeaderOptions, str]] = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ..., from_host: _Optional[str] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class SIPOutboundTrunkUpdate(_message.Message): + __slots__ = ("address", "transport", "destination_country", "numbers", "auth_username", "auth_password", "name", "metadata", "media_encryption", "media", "from_host") + ADDRESS_FIELD_NUMBER: _ClassVar[int] + TRANSPORT_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] + NUMBERS_FIELD_NUMBER: _ClassVar[int] + AUTH_USERNAME_FIELD_NUMBER: _ClassVar[int] + AUTH_PASSWORD_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + FROM_HOST_FIELD_NUMBER: _ClassVar[int] + address: str + transport: SIPTransport + destination_country: str + numbers: _models.ListUpdate + auth_username: str + auth_password: str + name: str + metadata: str + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + from_host: str + def __init__(self, address: _Optional[str] = ..., transport: _Optional[_Union[SIPTransport, str]] = ..., destination_country: _Optional[str] = ..., numbers: _Optional[_Union[_models.ListUpdate, _Mapping]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ..., from_host: _Optional[str] = ...) -> None: ... + +class GetSIPInboundTrunkRequest(_message.Message): + __slots__ = ("sip_trunk_id",) + SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + sip_trunk_id: str + def __init__(self, sip_trunk_id: _Optional[str] = ...) -> None: ... + +class GetSIPInboundTrunkResponse(_message.Message): + __slots__ = ("trunk",) + TRUNK_FIELD_NUMBER: _ClassVar[int] + trunk: SIPInboundTrunkInfo + def __init__(self, trunk: _Optional[_Union[SIPInboundTrunkInfo, _Mapping]] = ...) -> None: ... + +class GetSIPOutboundTrunkRequest(_message.Message): + __slots__ = ("sip_trunk_id",) + SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + sip_trunk_id: str + def __init__(self, sip_trunk_id: _Optional[str] = ...) -> None: ... + +class GetSIPOutboundTrunkResponse(_message.Message): + __slots__ = ("trunk",) + TRUNK_FIELD_NUMBER: _ClassVar[int] + trunk: SIPOutboundTrunkInfo + def __init__(self, trunk: _Optional[_Union[SIPOutboundTrunkInfo, _Mapping]] = ...) -> None: ... class ListSIPTrunkRequest(_message.Message): - __slots__ = () - def __init__(self) -> None: ... + __slots__ = ("page",) + PAGE_FIELD_NUMBER: _ClassVar[int] + page: _models.Pagination + def __init__(self, page: _Optional[_Union[_models.Pagination, _Mapping]] = ...) -> None: ... class ListSIPTrunkResponse(_message.Message): __slots__ = ("items",) @@ -146,8 +595,14 @@ class ListSIPTrunkResponse(_message.Message): def __init__(self, items: _Optional[_Iterable[_Union[SIPTrunkInfo, _Mapping]]] = ...) -> None: ... class ListSIPInboundTrunkRequest(_message.Message): - __slots__ = () - def __init__(self) -> None: ... + __slots__ = ("page", "trunk_ids", "numbers") + PAGE_FIELD_NUMBER: _ClassVar[int] + TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] + NUMBERS_FIELD_NUMBER: _ClassVar[int] + page: _models.Pagination + trunk_ids: _containers.RepeatedScalarFieldContainer[str] + numbers: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, page: _Optional[_Union[_models.Pagination, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., numbers: _Optional[_Iterable[str]] = ...) -> None: ... class ListSIPInboundTrunkResponse(_message.Message): __slots__ = ("items",) @@ -156,8 +611,14 @@ class ListSIPInboundTrunkResponse(_message.Message): def __init__(self, items: _Optional[_Iterable[_Union[SIPInboundTrunkInfo, _Mapping]]] = ...) -> None: ... class ListSIPOutboundTrunkRequest(_message.Message): - __slots__ = () - def __init__(self) -> None: ... + __slots__ = ("page", "trunk_ids", "numbers") + PAGE_FIELD_NUMBER: _ClassVar[int] + TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] + NUMBERS_FIELD_NUMBER: _ClassVar[int] + page: _models.Pagination + trunk_ids: _containers.RepeatedScalarFieldContainer[str] + numbers: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, page: _Optional[_Union[_models.Pagination, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., numbers: _Optional[_Iterable[str]] = ...) -> None: ... class ListSIPOutboundTrunkResponse(_message.Message): __slots__ = ("items",) @@ -180,23 +641,37 @@ class SIPDispatchRuleDirect(_message.Message): def __init__(self, room_name: _Optional[str] = ..., pin: _Optional[str] = ...) -> None: ... class SIPDispatchRuleIndividual(_message.Message): - __slots__ = ("room_prefix", "pin") + __slots__ = ("room_prefix", "pin", "no_randomness") + ROOM_PREFIX_FIELD_NUMBER: _ClassVar[int] + PIN_FIELD_NUMBER: _ClassVar[int] + NO_RANDOMNESS_FIELD_NUMBER: _ClassVar[int] + room_prefix: str + pin: str + no_randomness: bool + def __init__(self, room_prefix: _Optional[str] = ..., pin: _Optional[str] = ..., no_randomness: bool = ...) -> None: ... + +class SIPDispatchRuleCallee(_message.Message): + __slots__ = ("room_prefix", "pin", "randomize") ROOM_PREFIX_FIELD_NUMBER: _ClassVar[int] PIN_FIELD_NUMBER: _ClassVar[int] + RANDOMIZE_FIELD_NUMBER: _ClassVar[int] room_prefix: str pin: str - def __init__(self, room_prefix: _Optional[str] = ..., pin: _Optional[str] = ...) -> None: ... + randomize: bool + def __init__(self, room_prefix: _Optional[str] = ..., pin: _Optional[str] = ..., randomize: bool = ...) -> None: ... class SIPDispatchRule(_message.Message): - __slots__ = ("dispatch_rule_direct", "dispatch_rule_individual") + __slots__ = ("dispatch_rule_direct", "dispatch_rule_individual", "dispatch_rule_callee") DISPATCH_RULE_DIRECT_FIELD_NUMBER: _ClassVar[int] DISPATCH_RULE_INDIVIDUAL_FIELD_NUMBER: _ClassVar[int] + DISPATCH_RULE_CALLEE_FIELD_NUMBER: _ClassVar[int] dispatch_rule_direct: SIPDispatchRuleDirect dispatch_rule_individual: SIPDispatchRuleIndividual - def __init__(self, dispatch_rule_direct: _Optional[_Union[SIPDispatchRuleDirect, _Mapping]] = ..., dispatch_rule_individual: _Optional[_Union[SIPDispatchRuleIndividual, _Mapping]] = ...) -> None: ... + dispatch_rule_callee: SIPDispatchRuleCallee + def __init__(self, dispatch_rule_direct: _Optional[_Union[SIPDispatchRuleDirect, _Mapping]] = ..., dispatch_rule_individual: _Optional[_Union[SIPDispatchRuleIndividual, _Mapping]] = ..., dispatch_rule_callee: _Optional[_Union[SIPDispatchRuleCallee, _Mapping]] = ...) -> None: ... class CreateSIPDispatchRuleRequest(_message.Message): - __slots__ = ("rule", "trunk_ids", "hide_phone_number", "inbound_numbers", "name", "metadata", "attributes") + __slots__ = ("dispatch_rule", "rule", "trunk_ids", "hide_phone_number", "inbound_numbers", "name", "metadata", "attributes", "room_preset", "room_config") class AttributesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -204,6 +679,7 @@ class CreateSIPDispatchRuleRequest(_message.Message): key: str value: str def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + DISPATCH_RULE_FIELD_NUMBER: _ClassVar[int] RULE_FIELD_NUMBER: _ClassVar[int] TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] HIDE_PHONE_NUMBER_FIELD_NUMBER: _ClassVar[int] @@ -211,6 +687,9 @@ class CreateSIPDispatchRuleRequest(_message.Message): NAME_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ROOM_PRESET_FIELD_NUMBER: _ClassVar[int] + ROOM_CONFIG_FIELD_NUMBER: _ClassVar[int] + dispatch_rule: SIPDispatchRuleInfo rule: SIPDispatchRule trunk_ids: _containers.RepeatedScalarFieldContainer[str] hide_phone_number: bool @@ -218,10 +697,22 @@ class CreateSIPDispatchRuleRequest(_message.Message): name: str metadata: str attributes: _containers.ScalarMap[str, str] - def __init__(self, rule: _Optional[_Union[SIPDispatchRule, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., hide_phone_number: bool = ..., inbound_numbers: _Optional[_Iterable[str]] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + room_preset: str + room_config: _room.RoomConfiguration + def __init__(self, dispatch_rule: _Optional[_Union[SIPDispatchRuleInfo, _Mapping]] = ..., rule: _Optional[_Union[SIPDispatchRule, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., hide_phone_number: bool = ..., inbound_numbers: _Optional[_Iterable[str]] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ..., room_preset: _Optional[str] = ..., room_config: _Optional[_Union[_room.RoomConfiguration, _Mapping]] = ...) -> None: ... + +class UpdateSIPDispatchRuleRequest(_message.Message): + __slots__ = ("sip_dispatch_rule_id", "replace", "update") + SIP_DISPATCH_RULE_ID_FIELD_NUMBER: _ClassVar[int] + REPLACE_FIELD_NUMBER: _ClassVar[int] + UPDATE_FIELD_NUMBER: _ClassVar[int] + sip_dispatch_rule_id: str + replace: SIPDispatchRuleInfo + update: SIPDispatchRuleUpdate + def __init__(self, sip_dispatch_rule_id: _Optional[str] = ..., replace: _Optional[_Union[SIPDispatchRuleInfo, _Mapping]] = ..., update: _Optional[_Union[SIPDispatchRuleUpdate, _Mapping]] = ...) -> None: ... class SIPDispatchRuleInfo(_message.Message): - __slots__ = ("sip_dispatch_rule_id", "rule", "trunk_ids", "hide_phone_number", "inbound_numbers", "name", "metadata", "attributes") + __slots__ = ("sip_dispatch_rule_id", "rule", "trunk_ids", "hide_phone_number", "inbound_numbers", "numbers", "name", "metadata", "attributes", "room_preset", "room_config", "media", "krisp_enabled", "media_encryption", "created_at", "updated_at") class AttributesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -234,22 +725,69 @@ class SIPDispatchRuleInfo(_message.Message): TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] HIDE_PHONE_NUMBER_FIELD_NUMBER: _ClassVar[int] INBOUND_NUMBERS_FIELD_NUMBER: _ClassVar[int] + NUMBERS_FIELD_NUMBER: _ClassVar[int] NAME_FIELD_NUMBER: _ClassVar[int] METADATA_FIELD_NUMBER: _ClassVar[int] ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ROOM_PRESET_FIELD_NUMBER: _ClassVar[int] + ROOM_CONFIG_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + KRISP_ENABLED_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + UPDATED_AT_FIELD_NUMBER: _ClassVar[int] sip_dispatch_rule_id: str rule: SIPDispatchRule trunk_ids: _containers.RepeatedScalarFieldContainer[str] hide_phone_number: bool inbound_numbers: _containers.RepeatedScalarFieldContainer[str] + numbers: _containers.RepeatedScalarFieldContainer[str] name: str metadata: str attributes: _containers.ScalarMap[str, str] - def __init__(self, sip_dispatch_rule_id: _Optional[str] = ..., rule: _Optional[_Union[SIPDispatchRule, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., hide_phone_number: bool = ..., inbound_numbers: _Optional[_Iterable[str]] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ...) -> None: ... + room_preset: str + room_config: _room.RoomConfiguration + media: SIPMediaConfig + krisp_enabled: bool + media_encryption: SIPMediaEncryption + created_at: _timestamp_pb2.Timestamp + updated_at: _timestamp_pb2.Timestamp + def __init__(self, sip_dispatch_rule_id: _Optional[str] = ..., rule: _Optional[_Union[SIPDispatchRule, _Mapping]] = ..., trunk_ids: _Optional[_Iterable[str]] = ..., hide_phone_number: bool = ..., inbound_numbers: _Optional[_Iterable[str]] = ..., numbers: _Optional[_Iterable[str]] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ..., room_preset: _Optional[str] = ..., room_config: _Optional[_Union[_room.RoomConfiguration, _Mapping]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ..., krisp_enabled: bool = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., created_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ..., updated_at: _Optional[_Union[_timestamp_pb2.Timestamp, _Mapping]] = ...) -> None: ... + +class SIPDispatchRuleUpdate(_message.Message): + __slots__ = ("trunk_ids", "rule", "name", "metadata", "attributes", "media_encryption", "media") + class AttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] + RULE_FIELD_NUMBER: _ClassVar[int] + NAME_FIELD_NUMBER: _ClassVar[int] + METADATA_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + trunk_ids: _models.ListUpdate + rule: SIPDispatchRule + name: str + metadata: str + attributes: _containers.ScalarMap[str, str] + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + def __init__(self, trunk_ids: _Optional[_Union[_models.ListUpdate, _Mapping]] = ..., rule: _Optional[_Union[SIPDispatchRule, _Mapping]] = ..., name: _Optional[str] = ..., metadata: _Optional[str] = ..., attributes: _Optional[_Mapping[str, str]] = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ...) -> None: ... class ListSIPDispatchRuleRequest(_message.Message): - __slots__ = () - def __init__(self) -> None: ... + __slots__ = ("page", "dispatch_rule_ids", "trunk_ids") + PAGE_FIELD_NUMBER: _ClassVar[int] + DISPATCH_RULE_IDS_FIELD_NUMBER: _ClassVar[int] + TRUNK_IDS_FIELD_NUMBER: _ClassVar[int] + page: _models.Pagination + dispatch_rule_ids: _containers.RepeatedScalarFieldContainer[str] + trunk_ids: _containers.RepeatedScalarFieldContainer[str] + def __init__(self, page: _Optional[_Union[_models.Pagination, _Mapping]] = ..., dispatch_rule_ids: _Optional[_Iterable[str]] = ..., trunk_ids: _Optional[_Iterable[str]] = ...) -> None: ... class ListSIPDispatchRuleResponse(_message.Message): __slots__ = ("items",) @@ -263,8 +801,42 @@ class DeleteSIPDispatchRuleRequest(_message.Message): sip_dispatch_rule_id: str def __init__(self, sip_dispatch_rule_id: _Optional[str] = ...) -> None: ... +class SIPOutboundConfig(_message.Message): + __slots__ = ("hostname", "destination_country", "transport", "auth_username", "auth_password", "headers_to_attributes", "attributes_to_headers", "from_host") + class HeadersToAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class AttributesToHeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + HOSTNAME_FIELD_NUMBER: _ClassVar[int] + DESTINATION_COUNTRY_FIELD_NUMBER: _ClassVar[int] + TRANSPORT_FIELD_NUMBER: _ClassVar[int] + AUTH_USERNAME_FIELD_NUMBER: _ClassVar[int] + AUTH_PASSWORD_FIELD_NUMBER: _ClassVar[int] + HEADERS_TO_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + ATTRIBUTES_TO_HEADERS_FIELD_NUMBER: _ClassVar[int] + FROM_HOST_FIELD_NUMBER: _ClassVar[int] + hostname: str + destination_country: str + transport: SIPTransport + auth_username: str + auth_password: str + headers_to_attributes: _containers.ScalarMap[str, str] + attributes_to_headers: _containers.ScalarMap[str, str] + from_host: str + def __init__(self, hostname: _Optional[str] = ..., destination_country: _Optional[str] = ..., transport: _Optional[_Union[SIPTransport, str]] = ..., auth_username: _Optional[str] = ..., auth_password: _Optional[str] = ..., headers_to_attributes: _Optional[_Mapping[str, str]] = ..., attributes_to_headers: _Optional[_Mapping[str, str]] = ..., from_host: _Optional[str] = ...) -> None: ... + class CreateSIPParticipantRequest(_message.Message): - __slots__ = ("sip_trunk_id", "sip_call_to", "room_name", "participant_identity", "participant_name", "participant_metadata", "participant_attributes", "dtmf", "play_ringtone", "hide_phone_number") + __slots__ = ("sip_trunk_id", "trunk", "sip_request_uri", "sip_to_header", "sip_from_header", "sip_call_to", "sip_number", "room_name", "participant_identity", "participant_name", "participant_metadata", "participant_attributes", "dtmf", "play_ringtone", "play_dialtone", "hide_phone_number", "headers", "include_headers", "ringing_timeout", "max_call_duration", "krisp_enabled", "media_encryption", "media", "wait_until_answered", "display_name", "destination") class ParticipantAttributesEntry(_message.Message): __slots__ = ("key", "value") KEY_FIELD_NUMBER: _ClassVar[int] @@ -272,8 +844,20 @@ class CreateSIPParticipantRequest(_message.Message): key: str value: str def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... SIP_TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + TRUNK_FIELD_NUMBER: _ClassVar[int] + SIP_REQUEST_URI_FIELD_NUMBER: _ClassVar[int] + SIP_TO_HEADER_FIELD_NUMBER: _ClassVar[int] + SIP_FROM_HEADER_FIELD_NUMBER: _ClassVar[int] SIP_CALL_TO_FIELD_NUMBER: _ClassVar[int] + SIP_NUMBER_FIELD_NUMBER: _ClassVar[int] ROOM_NAME_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] PARTICIPANT_NAME_FIELD_NUMBER: _ClassVar[int] @@ -281,9 +865,25 @@ class CreateSIPParticipantRequest(_message.Message): PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] DTMF_FIELD_NUMBER: _ClassVar[int] PLAY_RINGTONE_FIELD_NUMBER: _ClassVar[int] + PLAY_DIALTONE_FIELD_NUMBER: _ClassVar[int] HIDE_PHONE_NUMBER_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + INCLUDE_HEADERS_FIELD_NUMBER: _ClassVar[int] + RINGING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + MAX_CALL_DURATION_FIELD_NUMBER: _ClassVar[int] + KRISP_ENABLED_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + MEDIA_FIELD_NUMBER: _ClassVar[int] + WAIT_UNTIL_ANSWERED_FIELD_NUMBER: _ClassVar[int] + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + DESTINATION_FIELD_NUMBER: _ClassVar[int] sip_trunk_id: str + trunk: SIPOutboundConfig + sip_request_uri: SIPRequestDest + sip_to_header: SIPNamedDest + sip_from_header: SIPNamedDest sip_call_to: str + sip_number: str room_name: str participant_identity: str participant_name: str @@ -291,8 +891,19 @@ class CreateSIPParticipantRequest(_message.Message): participant_attributes: _containers.ScalarMap[str, str] dtmf: str play_ringtone: bool + play_dialtone: bool hide_phone_number: bool - def __init__(self, sip_trunk_id: _Optional[str] = ..., sip_call_to: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., dtmf: _Optional[str] = ..., play_ringtone: bool = ..., hide_phone_number: bool = ...) -> None: ... + headers: _containers.ScalarMap[str, str] + include_headers: SIPHeaderOptions + ringing_timeout: _duration_pb2.Duration + max_call_duration: _duration_pb2.Duration + krisp_enabled: bool + media_encryption: SIPMediaEncryption + media: SIPMediaConfig + wait_until_answered: bool + display_name: str + destination: Destination + def __init__(self, sip_trunk_id: _Optional[str] = ..., trunk: _Optional[_Union[SIPOutboundConfig, _Mapping]] = ..., sip_request_uri: _Optional[_Union[SIPRequestDest, _Mapping]] = ..., sip_to_header: _Optional[_Union[SIPNamedDest, _Mapping]] = ..., sip_from_header: _Optional[_Union[SIPNamedDest, _Mapping]] = ..., sip_call_to: _Optional[str] = ..., sip_number: _Optional[str] = ..., room_name: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_name: _Optional[str] = ..., participant_metadata: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., dtmf: _Optional[str] = ..., play_ringtone: bool = ..., play_dialtone: bool = ..., hide_phone_number: bool = ..., headers: _Optional[_Mapping[str, str]] = ..., include_headers: _Optional[_Union[SIPHeaderOptions, str]] = ..., ringing_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., max_call_duration: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ..., krisp_enabled: bool = ..., media_encryption: _Optional[_Union[SIPMediaEncryption, str]] = ..., media: _Optional[_Union[SIPMediaConfig, _Mapping]] = ..., wait_until_answered: bool = ..., display_name: _Optional[str] = ..., destination: _Optional[_Union[Destination, _Mapping]] = ...) -> None: ... class SIPParticipantInfo(_message.Message): __slots__ = ("participant_id", "participant_identity", "room_name", "sip_call_id") @@ -305,3 +916,155 @@ class SIPParticipantInfo(_message.Message): room_name: str sip_call_id: str def __init__(self, participant_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., room_name: _Optional[str] = ..., sip_call_id: _Optional[str] = ...) -> None: ... + +class TransferSIPParticipantRequest(_message.Message): + __slots__ = ("participant_identity", "room_name", "transfer_to", "play_dialtone", "headers", "ringing_timeout") + class HeadersEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + TRANSFER_TO_FIELD_NUMBER: _ClassVar[int] + PLAY_DIALTONE_FIELD_NUMBER: _ClassVar[int] + HEADERS_FIELD_NUMBER: _ClassVar[int] + RINGING_TIMEOUT_FIELD_NUMBER: _ClassVar[int] + participant_identity: str + room_name: str + transfer_to: str + play_dialtone: bool + headers: _containers.ScalarMap[str, str] + ringing_timeout: _duration_pb2.Duration + def __init__(self, participant_identity: _Optional[str] = ..., room_name: _Optional[str] = ..., transfer_to: _Optional[str] = ..., play_dialtone: bool = ..., headers: _Optional[_Mapping[str, str]] = ..., ringing_timeout: _Optional[_Union[_duration_pb2.Duration, _Mapping]] = ...) -> None: ... + +class SIPCallInfo(_message.Message): + __slots__ = ("call_id", "trunk_id", "dispatch_rule_id", "region", "room_name", "room_id", "participant_identity", "participant_attributes", "from_uri", "to_uri", "created_at", "started_at", "ended_at", "enabled_features", "call_direction", "call_status", "created_at_ns", "started_at_ns", "ended_at_ns", "disconnect_reason", "error", "call_status_code", "audio_codec", "media_encryption", "pcap_file_link", "call_context", "provider_info", "sip_call_id") + class ParticipantAttributesEntry(_message.Message): + __slots__ = ("key", "value") + KEY_FIELD_NUMBER: _ClassVar[int] + VALUE_FIELD_NUMBER: _ClassVar[int] + key: str + value: str + def __init__(self, key: _Optional[str] = ..., value: _Optional[str] = ...) -> None: ... + CALL_ID_FIELD_NUMBER: _ClassVar[int] + TRUNK_ID_FIELD_NUMBER: _ClassVar[int] + DISPATCH_RULE_ID_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + ROOM_NAME_FIELD_NUMBER: _ClassVar[int] + ROOM_ID_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_IDENTITY_FIELD_NUMBER: _ClassVar[int] + PARTICIPANT_ATTRIBUTES_FIELD_NUMBER: _ClassVar[int] + FROM_URI_FIELD_NUMBER: _ClassVar[int] + TO_URI_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_FIELD_NUMBER: _ClassVar[int] + STARTED_AT_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_FIELD_NUMBER: _ClassVar[int] + ENABLED_FEATURES_FIELD_NUMBER: _ClassVar[int] + CALL_DIRECTION_FIELD_NUMBER: _ClassVar[int] + CALL_STATUS_FIELD_NUMBER: _ClassVar[int] + CREATED_AT_NS_FIELD_NUMBER: _ClassVar[int] + STARTED_AT_NS_FIELD_NUMBER: _ClassVar[int] + ENDED_AT_NS_FIELD_NUMBER: _ClassVar[int] + DISCONNECT_REASON_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + CALL_STATUS_CODE_FIELD_NUMBER: _ClassVar[int] + AUDIO_CODEC_FIELD_NUMBER: _ClassVar[int] + MEDIA_ENCRYPTION_FIELD_NUMBER: _ClassVar[int] + PCAP_FILE_LINK_FIELD_NUMBER: _ClassVar[int] + CALL_CONTEXT_FIELD_NUMBER: _ClassVar[int] + PROVIDER_INFO_FIELD_NUMBER: _ClassVar[int] + SIP_CALL_ID_FIELD_NUMBER: _ClassVar[int] + call_id: str + trunk_id: str + dispatch_rule_id: str + region: str + room_name: str + room_id: str + participant_identity: str + participant_attributes: _containers.ScalarMap[str, str] + from_uri: SIPUri + to_uri: SIPUri + created_at: int + started_at: int + ended_at: int + enabled_features: _containers.RepeatedScalarFieldContainer[SIPFeature] + call_direction: SIPCallDirection + call_status: SIPCallStatus + created_at_ns: int + started_at_ns: int + ended_at_ns: int + disconnect_reason: _models.DisconnectReason + error: str + call_status_code: SIPStatus + audio_codec: str + media_encryption: str + pcap_file_link: str + call_context: _containers.RepeatedCompositeFieldContainer[_any_pb2.Any] + provider_info: ProviderInfo + sip_call_id: str + def __init__(self, call_id: _Optional[str] = ..., trunk_id: _Optional[str] = ..., dispatch_rule_id: _Optional[str] = ..., region: _Optional[str] = ..., room_name: _Optional[str] = ..., room_id: _Optional[str] = ..., participant_identity: _Optional[str] = ..., participant_attributes: _Optional[_Mapping[str, str]] = ..., from_uri: _Optional[_Union[SIPUri, _Mapping]] = ..., to_uri: _Optional[_Union[SIPUri, _Mapping]] = ..., created_at: _Optional[int] = ..., started_at: _Optional[int] = ..., ended_at: _Optional[int] = ..., enabled_features: _Optional[_Iterable[_Union[SIPFeature, str]]] = ..., call_direction: _Optional[_Union[SIPCallDirection, str]] = ..., call_status: _Optional[_Union[SIPCallStatus, str]] = ..., created_at_ns: _Optional[int] = ..., started_at_ns: _Optional[int] = ..., ended_at_ns: _Optional[int] = ..., disconnect_reason: _Optional[_Union[_models.DisconnectReason, str]] = ..., error: _Optional[str] = ..., call_status_code: _Optional[_Union[SIPStatus, _Mapping]] = ..., audio_codec: _Optional[str] = ..., media_encryption: _Optional[str] = ..., pcap_file_link: _Optional[str] = ..., call_context: _Optional[_Iterable[_Union[_any_pb2.Any, _Mapping]]] = ..., provider_info: _Optional[_Union[ProviderInfo, _Mapping]] = ..., sip_call_id: _Optional[str] = ...) -> None: ... + +class SIPTransferInfo(_message.Message): + __slots__ = ("transfer_id", "call_id", "transfer_to", "transfer_initiated_at_ns", "transfer_completed_at_ns", "transfer_status", "error", "transfer_status_code") + TRANSFER_ID_FIELD_NUMBER: _ClassVar[int] + CALL_ID_FIELD_NUMBER: _ClassVar[int] + TRANSFER_TO_FIELD_NUMBER: _ClassVar[int] + TRANSFER_INITIATED_AT_NS_FIELD_NUMBER: _ClassVar[int] + TRANSFER_COMPLETED_AT_NS_FIELD_NUMBER: _ClassVar[int] + TRANSFER_STATUS_FIELD_NUMBER: _ClassVar[int] + ERROR_FIELD_NUMBER: _ClassVar[int] + TRANSFER_STATUS_CODE_FIELD_NUMBER: _ClassVar[int] + transfer_id: str + call_id: str + transfer_to: str + transfer_initiated_at_ns: int + transfer_completed_at_ns: int + transfer_status: SIPTransferStatus + error: str + transfer_status_code: SIPStatus + def __init__(self, transfer_id: _Optional[str] = ..., call_id: _Optional[str] = ..., transfer_to: _Optional[str] = ..., transfer_initiated_at_ns: _Optional[int] = ..., transfer_completed_at_ns: _Optional[int] = ..., transfer_status: _Optional[_Union[SIPTransferStatus, str]] = ..., error: _Optional[str] = ..., transfer_status_code: _Optional[_Union[SIPStatus, _Mapping]] = ...) -> None: ... + +class SIPUri(_message.Message): + __slots__ = ("user", "host", "ip", "port", "transport") + USER_FIELD_NUMBER: _ClassVar[int] + HOST_FIELD_NUMBER: _ClassVar[int] + IP_FIELD_NUMBER: _ClassVar[int] + PORT_FIELD_NUMBER: _ClassVar[int] + TRANSPORT_FIELD_NUMBER: _ClassVar[int] + user: str + host: str + ip: str + port: int + transport: SIPTransport + def __init__(self, user: _Optional[str] = ..., host: _Optional[str] = ..., ip: _Optional[str] = ..., port: _Optional[int] = ..., transport: _Optional[_Union[SIPTransport, str]] = ...) -> None: ... + +class SIPRequestDest(_message.Message): + __slots__ = ("raw", "values") + RAW_FIELD_NUMBER: _ClassVar[int] + VALUES_FIELD_NUMBER: _ClassVar[int] + raw: str + values: SIPUri + def __init__(self, raw: _Optional[str] = ..., values: _Optional[_Union[SIPUri, _Mapping]] = ...) -> None: ... + +class SIPNamedDest(_message.Message): + __slots__ = ("display_name", "raw", "values") + DISPLAY_NAME_FIELD_NUMBER: _ClassVar[int] + RAW_FIELD_NUMBER: _ClassVar[int] + VALUES_FIELD_NUMBER: _ClassVar[int] + display_name: str + raw: str + values: SIPUri + def __init__(self, display_name: _Optional[str] = ..., raw: _Optional[str] = ..., values: _Optional[_Union[SIPUri, _Mapping]] = ...) -> None: ... + +class Destination(_message.Message): + __slots__ = ("city", "country", "region") + CITY_FIELD_NUMBER: _ClassVar[int] + COUNTRY_FIELD_NUMBER: _ClassVar[int] + REGION_FIELD_NUMBER: _ClassVar[int] + city: str + country: str + region: str + def __init__(self, city: _Optional[str] = ..., country: _Optional[str] = ..., region: _Optional[str] = ...) -> None: ... diff --git a/livekit-protocol/livekit/protocol/version.py b/livekit-protocol/livekit/protocol/version.py index dd9b22cc..209742c7 100644 --- a/livekit-protocol/livekit/protocol/version.py +++ b/livekit-protocol/livekit/protocol/version.py @@ -1 +1 @@ -__version__ = "0.5.1" +__version__ = "1.1.22" diff --git a/livekit-protocol/livekit/protocol/webhook.py b/livekit-protocol/livekit/protocol/webhook.py index 7c9f12c4..a0a5f681 100644 --- a/livekit-protocol/livekit/protocol/webhook.py +++ b/livekit-protocol/livekit/protocol/webhook.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: livekit_webhook.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 5.29.3 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 3, + '', + 'livekit_webhook.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -17,14 +27,16 @@ from . import ingress as _ingress_ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15livekit_webhook.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14livekit_egress.proto\x1a\x15livekit_ingress.proto\"\x97\x02\n\x0cWebhookEvent\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x1b\n\x04room\x18\x02 \x01(\x0b\x32\r.livekit.Room\x12-\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12(\n\x0b\x65gress_info\x18\t \x01(\x0b\x32\x13.livekit.EgressInfo\x12*\n\x0cingress_info\x18\n \x01(\x0b\x32\x14.livekit.IngressInfo\x12!\n\x05track\x18\x08 \x01(\x0b\x32\x12.livekit.TrackInfo\x12\n\n\x02id\x18\x06 \x01(\t\x12\x12\n\ncreated_at\x18\x07 \x01(\x03\x12\x13\n\x0bnum_dropped\x18\x0b \x01(\x05\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x15livekit_webhook.proto\x12\x07livekit\x1a\x14livekit_models.proto\x1a\x14livekit_egress.proto\x1a\x15livekit_ingress.proto\"\x9b\x02\n\x0cWebhookEvent\x12\r\n\x05\x65vent\x18\x01 \x01(\t\x12\x1b\n\x04room\x18\x02 \x01(\x0b\x32\r.livekit.Room\x12-\n\x0bparticipant\x18\x03 \x01(\x0b\x32\x18.livekit.ParticipantInfo\x12(\n\x0b\x65gress_info\x18\t \x01(\x0b\x32\x13.livekit.EgressInfo\x12*\n\x0cingress_info\x18\n \x01(\x0b\x32\x14.livekit.IngressInfo\x12!\n\x05track\x18\x08 \x01(\x0b\x32\x12.livekit.TrackInfo\x12\n\n\x02id\x18\x06 \x01(\t\x12\x12\n\ncreated_at\x18\x07 \x01(\x03\x12\x17\n\x0bnum_dropped\x18\x0b \x01(\x05\x42\x02\x18\x01\x42\x46Z#github.com/livekit/protocol/livekit\xaa\x02\rLiveKit.Proto\xea\x02\x0eLiveKit::Protob\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'webhook', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - _globals['DESCRIPTOR']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + _globals['DESCRIPTOR']._loaded_options = None _globals['DESCRIPTOR']._serialized_options = b'Z#github.com/livekit/protocol/livekit\252\002\rLiveKit.Proto\352\002\016LiveKit::Proto' + _globals['_WEBHOOKEVENT'].fields_by_name['num_dropped']._loaded_options = None + _globals['_WEBHOOKEVENT'].fields_by_name['num_dropped']._serialized_options = b'\030\001' _globals['_WEBHOOKEVENT']._serialized_start=102 - _globals['_WEBHOOKEVENT']._serialized_end=381 + _globals['_WEBHOOKEVENT']._serialized_end=385 # @@protoc_insertion_point(module_scope) diff --git a/livekit-protocol/protocol b/livekit-protocol/protocol index 79b5ea9e..81425864 160000 --- a/livekit-protocol/protocol +++ b/livekit-protocol/protocol @@ -1 +1 @@ -Subproject commit 79b5ea9ea9a29991ca6819323173e080aae9cfd3 +Subproject commit 814258644c5d86fec64a24f5cd617411d3c6890b diff --git a/livekit-protocol/pyproject.toml b/livekit-protocol/pyproject.toml new file mode 100644 index 00000000..79eb84b0 --- /dev/null +++ b/livekit-protocol/pyproject.toml @@ -0,0 +1,44 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "livekit-protocol" +dynamic = ["version"] +description = "Python protocol stubs for LiveKit" +requires-python = ">=3.9.0" +license = "Apache-2.0" +readme = { content-type = "text/plain", text = "Python protocol stubs for LiveKit" } +keywords = ["webrtc", "realtime", "audio", "video", "livekit"] +authors = [ + { name = "LiveKit", email = "support@livekit.io" } +] +classifiers = [ + "Intended Audience :: Developers", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3 :: Only", +] +dependencies = [ + "protobuf>=5", + "types-protobuf>=5", +] + +[project.urls] +Documentation = "https://docs.livekit.io" +Website = "https://livekit.io/" +Source = "https://github.com/livekit/python-sdks/" + +[tool.hatch.version] +path = "livekit/protocol/version.py" + +[tool.hatch.build.targets.wheel] +packages = ["livekit"] + +[tool.hatch.build.targets.sdist] +include = ["/livekit"] diff --git a/livekit-protocol/setup.py b/livekit-protocol/setup.py deleted file mode 100644 index 9bec5035..00000000 --- a/livekit-protocol/setup.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2023 LiveKit, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import os -import pathlib -from typing import Any, Dict - -import setuptools # type: ignore - -here = pathlib.Path(__file__).parent.resolve() -about: Dict[Any, Any] = {} -with open(os.path.join(here, "livekit", "protocol", "version.py"), "r") as f: - exec(f.read(), about) - - -setuptools.setup( - name="livekit-protocol", - version=about["__version__"], - description="Python protocol stubs for LiveKit", - long_description="Python protocol stubs for LiveKit", - long_description_content_type="text/markdown", - url="https://github.com/livekit/python-sdks", - classifiers=[ - "Intended Audience :: Developers", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3 :: Only", - ], - keywords=["webrtc", "realtime", "audio", "video", "livekit"], - license="Apache-2.0", - packages=setuptools.find_namespace_packages(include=["livekit.*"]), - python_requires=">=3.7.0", - install_requires=[ - "protobuf>=3", - "types-protobuf>=4,<5", - ], - package_data={ - "livekit.protocol": ["*.pyi", "**/*.pyi", "py.typed"], - }, - project_urls={ - "Documentation": "https://docs.livekit.io", - "Website": "https://livekit.io/", - "Source": "https://github.com/livekit/python-sdks/", - }, -) diff --git a/livekit-rtc/README.md b/livekit-rtc/README.md index 105e723f..516f9b6c 100644 --- a/livekit-rtc/README.md +++ b/livekit-rtc/README.md @@ -1,3 +1,6 @@ -# LiveKit Real-time Python SDK +# LiveKit SDK for Python + +Python SDK to integrate LiveKit's real-time video, audio, and data capabilities into your Python applications using WebRTC. Designed for use with [LiveKit Agents](https://github.com/livekit/agents) to build powerful voice AI apps. + +See https://docs.livekit.io/ for more information. -The LiveKit Python SDK provides a convenient interface for integrating LiveKit's real-time video and audio capabilities into your Python applications. With it, developers can easily leverage LiveKit's WebRTC functionalities, allowing them to focus on building their AI models or other application logic without worrying about the complexities of WebRTC. diff --git a/livekit-rtc/generate_proto.sh b/livekit-rtc/generate_proto.sh index 1c4d0f03..76d8bb8a 100755 --- a/livekit-rtc/generate_proto.sh +++ b/livekit-rtc/generate_proto.sh @@ -12,7 +12,9 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +# # This script requires protobuf-compiler and https://github.com/nipunn1313/mypy-protobuf +# (Used by the build-rtc CI) FFI_PROTOCOL=./rust-sdks/livekit-ffi/protocol FFI_OUT_PYTHON=./livekit/rtc/_proto @@ -29,12 +31,16 @@ protoc \ $FFI_PROTOCOL/participant.proto \ $FFI_PROTOCOL/room.proto \ $FFI_PROTOCOL/track.proto \ + $FFI_PROTOCOL/data_track.proto \ $FFI_PROTOCOL/video_frame.proto \ $FFI_PROTOCOL/e2ee.proto \ - $FFI_PROTOCOL/stats.proto + $FFI_PROTOCOL/stats.proto \ + $FFI_PROTOCOL/track_publication.proto \ + $FFI_PROTOCOL/rpc.proto \ + $FFI_PROTOCOL/data_stream.proto touch -a "$FFI_OUT_PYTHON/__init__.py" for f in "$FFI_OUT_PYTHON"/*.py "$FFI_OUT_PYTHON"/*.pyi; do - perl -i -pe 's|^(import (audio_frame_pb2\|ffi_pb2\|handle_pb2\|participant_pb2\|room_pb2\|track_pb2\|video_frame_pb2\|e2ee_pb2\|stats_pb2))|from . $1|g' "$f" + perl -i -pe 's|^(import (audio_frame_pb2\|ffi_pb2\|handle_pb2\|participant_pb2\|room_pb2\|track_pb2\|video_frame_pb2\|e2ee_pb2\|stats_pb2\|rpc_pb2\|track_publication_pb2\|data_stream_pb2\|data_track_pb2))|from . $1|g' "$f" done diff --git a/livekit-rtc/jupyter-html/.gitignore b/livekit-rtc/jupyter-html/.gitignore new file mode 100644 index 00000000..a547bf36 --- /dev/null +++ b/livekit-rtc/jupyter-html/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/livekit-rtc/jupyter-html/README.md b/livekit-rtc/jupyter-html/README.md new file mode 100644 index 00000000..40ede56e --- /dev/null +++ b/livekit-rtc/jupyter-html/README.md @@ -0,0 +1,54 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git a/livekit-rtc/jupyter-html/eslint.config.js b/livekit-rtc/jupyter-html/eslint.config.js new file mode 100644 index 00000000..092408a9 --- /dev/null +++ b/livekit-rtc/jupyter-html/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/livekit-rtc/jupyter-html/index.html b/livekit-rtc/jupyter-html/index.html new file mode 100644 index 00000000..e4b78eae --- /dev/null +++ b/livekit-rtc/jupyter-html/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/livekit-rtc/jupyter-html/package.json b/livekit-rtc/jupyter-html/package.json new file mode 100644 index 00000000..37146441 --- /dev/null +++ b/livekit-rtc/jupyter-html/package.json @@ -0,0 +1,33 @@ +{ + "name": "jupyter-html", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build --outDir=../livekit/rtc/resources/jupyter-html", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@livekit/components-react": "^2.9.20", + "@livekit/components-styles": "^1.2.0", + "livekit-client": "^2.17.2", + "react": "^19.2.4", + "react-dom": "^19.2.4" + }, + "devDependencies": { + "@eslint/js": "^9.39.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^4.7.0", + "eslint": "^9.39.3", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.5.0", + "globals": "^17.0.0", + "typescript": "~5.9.0", + "typescript-eslint": "^8.56.1", + "vite": "^6.4.1", + "vite-plugin-singlefile": "^2.3.0" + } +} \ No newline at end of file diff --git a/livekit-rtc/jupyter-html/pnpm-lock.yaml b/livekit-rtc/jupyter-html/pnpm-lock.yaml new file mode 100644 index 00000000..2ff5abf1 --- /dev/null +++ b/livekit-rtc/jupyter-html/pnpm-lock.yaml @@ -0,0 +1,2296 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@livekit/components-react': + specifier: ^2.9.20 + version: 2.9.21(livekit-client@2.20.0(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1) + '@livekit/components-styles': + specifier: ^1.2.0 + version: 1.2.0 + livekit-client: + specifier: ^2.17.2 + version: 2.20.0(@types/dom-mediacapture-record@1.0.22) + react: + specifier: ^19.2.4 + version: 19.2.5 + react-dom: + specifier: ^19.2.4 + version: 19.2.5(react@19.2.5) + devDependencies: + '@eslint/js': + specifier: ^9.39.3 + version: 9.39.4 + '@types/react': + specifier: ^19.2.14 + version: 19.2.14 + '@types/react-dom': + specifier: ^19.2.3 + version: 19.2.3(@types/react@19.2.14) + '@vitejs/plugin-react': + specifier: ^4.7.0 + version: 4.7.0(supports-color@7.2.0)(vite@6.4.3) + eslint: + specifier: ^9.39.3 + version: 9.39.4(supports-color@7.2.0) + eslint-plugin-react-hooks: + specifier: ^5.2.0 + version: 5.2.0(eslint@9.39.4(supports-color@7.2.0)) + eslint-plugin-react-refresh: + specifier: ^0.5.0 + version: 0.5.2(eslint@9.39.4(supports-color@7.2.0)) + globals: + specifier: ^17.0.0 + version: 17.6.0 + typescript: + specifier: ~5.9.0 + version: 5.9.3 + typescript-eslint: + specifier: ^8.56.1 + version: 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + vite: + specifier: ^6.4.1 + version: 6.4.3 + vite-plugin-singlefile: + specifier: ^2.3.0 + version: 2.3.3(rollup@4.62.0)(vite@6.4.3) + +packages: + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.0': + resolution: {integrity: sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.28.6': + resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.6': + resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.0': + resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.27.1': + resolution: {integrity: sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.27.1': + resolution: {integrity: sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@bufbuild/protobuf@1.10.1': + resolution: {integrity: sha512-wJ8ReQbHxsAfXhrf9ixl0aYbZorRuOWpBNzm8pL8ftmSxQx/wnJD5Eg861NwJU/czy2VXFIebCeZnZrI9rktIQ==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@livekit/components-core@0.12.13': + resolution: {integrity: sha512-DQmi84afHoHjZ62wm8y+XPNIDHTwFHAltjd3lmyXj8UZHOY7wcza4vFt1xnghJOD5wLRY58L1dkAgAw59MgWvw==} + engines: {node: '>=18'} + peerDependencies: + livekit-client: ^2.17.2 + tslib: ^2.6.2 + + '@livekit/components-react@2.9.21': + resolution: {integrity: sha512-6hU9VucJJL+gAhilNGe4MBCDCZVk64qyjP9Ck86krvOIdVU76WeWksddg1MYUP10AlUwwrfD7davz41pJTcMJw==} + engines: {node: '>=18'} + peerDependencies: + '@livekit/krisp-noise-filter': ^0.2.12 || ^0.3.0 + livekit-client: ^2.18.2 + react: '>=18' + react-dom: '>=18' + tslib: ^2.6.2 + peerDependenciesMeta: + '@livekit/krisp-noise-filter': + optional: true + + '@livekit/components-styles@1.2.0': + resolution: {integrity: sha512-74/rt0lDh6aHmOPmWAeDE9C4OrNW9RIdmhX/YRbovQBVNGNVWojRjl3FgQZ5LPFXO6l1maKB4JhXcBFENVxVvw==} + engines: {node: '>=18'} + + '@livekit/mutex@1.1.1': + resolution: {integrity: sha512-EsshAucklmpuUAfkABPxJNhzj9v2sG7JuzFDL4ML1oJQSV14sqrpTYnsaOudMAw9yOaW53NU3QQTlUQoRs4czw==} + + '@livekit/protocol@1.46.6': + resolution: {integrity: sha512-upzlHP1vi/kZ/QqALZTFskQ0ifqc2f15RKucHYOsIHJsaXvEYanG75mAb7o+Yomfs4XhQ4BaRsdY+TFHXpaqrg==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.62.0': + resolution: {integrity: sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.0': + resolution: {integrity: sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.0': + resolution: {integrity: sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.0': + resolution: {integrity: sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.0': + resolution: {integrity: sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.0': + resolution: {integrity: sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + resolution: {integrity: sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm-musleabihf@4.62.0': + resolution: {integrity: sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-arm64-gnu@4.62.0': + resolution: {integrity: sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-arm64-musl@4.62.0': + resolution: {integrity: sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-loong64-gnu@4.62.0': + resolution: {integrity: sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-loong64-musl@4.62.0': + resolution: {integrity: sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-ppc64-gnu@4.62.0': + resolution: {integrity: sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-ppc64-musl@4.62.0': + resolution: {integrity: sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==} + cpu: [ppc64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-riscv64-gnu@4.62.0': + resolution: {integrity: sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-riscv64-musl@4.62.0': + resolution: {integrity: sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@rollup/rollup-linux-s390x-gnu@4.62.0': + resolution: {integrity: sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-gnu@4.62.0': + resolution: {integrity: sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@rollup/rollup-linux-x64-musl@4.62.0': + resolution: {integrity: sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@rollup/rollup-openbsd-x64@4.62.0': + resolution: {integrity: sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.0': + resolution: {integrity: sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.0': + resolution: {integrity: sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.0': + resolution: {integrity: sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.0': + resolution: {integrity: sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.0': + resolution: {integrity: sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==} + cpu: [x64] + os: [win32] + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/dom-mediacapture-record@1.0.22': + resolution: {integrity: sha512-mUMZLK3NvwRLcAAT9qmcK+9p7tpU2FHdDsntR3YI4+GY88XrgG4XiE7u1Q2LAN2/FZOz/tdMDC3GQCR4T8nFuw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@typescript-eslint/eslint-plugin@8.59.2': + resolution: {integrity: sha512-j/bwmkBvHUtPNxzuWe5z6BEk3q54YRyGlBXkSsmfoih7zNrBvl5A9A98anlp/7JbyZcWIJ8KXo/3Tq/DjFLtuQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.2 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.2': + resolution: {integrity: sha512-plR3pp6D+SSUn1HM7xvSkx12/DhoHInI2YF35KAcVFNZvlC0gtrWqx7Qq1oH2Ssgi0vlFRCTbP+DZc7B9+TtsQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.2': + resolution: {integrity: sha512-+2hqvEkeyf/0FBor67duF0Ll7Ot8jyKzDQOSrxazF/danillRq2DwR9dLptsXpoZQqxE1UisSmoZewrlPas9Vw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.2': + resolution: {integrity: sha512-JzfyEpEtOU89CcFSwyNS3mu4MLvLSXqnmX05+aKBDM+TdR5jzcGOEBwxwGNxrEQ7p/z6kK2WyioCGBf2zZBnvg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.2': + resolution: {integrity: sha512-BKK4alN7oi4C/zv4VqHQ+uRU+lTa6JGIZ7s1juw7b3RHo9OfKB+bKX3u0iVZetdsUCBBkSbdWbarJbmN0fTeSw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.2': + resolution: {integrity: sha512-nhqaj1nmTdVVl/BP5omXNRGO38jn5iosis2vbdmupF2txCf8ylWT8lx+JlvMYYVqzGVKtjojUFoQ3JRWK+mfzQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.2': + resolution: {integrity: sha512-e82GVOE8Ps3E++Egvb6Y3Dw0S10u8NkQ9KXmtRhCWJJ8kDhOJTvtMAWnFL16kB1583goCWXsr0NieKCZMs2/0Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.2': + resolution: {integrity: sha512-o0XPGNwcWw+FIwStOWn+BwBuEmL6QXP0rsvAFg7ET1dey1Nr6Wb1ac8p5HEsK0ygO/6mUxlk+YWQD9xcb/nnXg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.2': + resolution: {integrity: sha512-Juw3EinkXqjaffxz6roowvV7GZT/kET5vSKKZT6upl5TXdWkLkYmNPXwDDL2Vkt2DPn0nODIS4egC/0AGxKo/Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.2': + resolution: {integrity: sha512-NwjLUnGy8/Zfx23fl50tRC8rYaYnM52xNRYFAXvmiil9yh1+K6aRVQMnzW6gQB/1DLgWt977lYQn7C+wtgXZiA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.14.0: + resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.0: + resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001774: + resolution: {integrity: sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + electron-to-chromium@1.5.302: + resolution: {integrity: sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react-refresh@0.5.2: + resolution: {integrity: sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==} + peerDependencies: + eslint: ^9 || ^10 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@17.6.0: + resolution: {integrity: sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==} + engines: {node: '>=18'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jose@6.2.3: + resolution: {integrity: sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + livekit-client@2.20.0: + resolution: {integrity: sha512-RIJcpvBmOmwz3jTj3rmdY6Dzr55HrhcaJjMgY+HSmoEM+yIRyA40m7r8UKv0hnZWM3z/AYhP1q8C8ciz5UWFKQ==} + peerDependencies: + '@types/dom-mediacapture-record': ^1 + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loglevel@1.9.1: + resolution: {integrity: sha512-hP3I3kCrDIMuRwAwHltphhDM1r8i55H33GgqjXbrisuJhF4kRhW1dNuxsRklp4bXl8DSdLaNLuiL4A/LWRfxvg==} + engines: {node: '>= 0.6.0'} + + loglevel@1.9.2: + resolution: {integrity: sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==} + engines: {node: '>= 0.6.0'} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + react-dom@19.2.5: + resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + peerDependencies: + react: ^19.2.5 + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@19.2.5: + resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + rollup@4.62.0: + resolution: {integrity: sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + rxjs@7.8.2: + resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + sdp-transform@2.15.0: + resolution: {integrity: sha512-KrOH82c/W+GYQ0LHqtr3caRpM3ITglq3ljGUIb8LTki7ByacJZ9z+piSGiwZDsRyhQbYBOBJgr2k6X4BZXi3Kw==} + hasBin: true + + sdp@3.2.2: + resolution: {integrity: sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-emitter@2.1.0: + resolution: {integrity: sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==} + + typescript-eslint@8.59.2: + resolution: {integrity: sha512-pJw051uomb3ZeCzGTpRb8RbEqB5Y4WWet8gl/GcTlU35BSx0PVdZ86/bqkQCyKKuraVQEK7r6kBHQXF+fBhkoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + usehooks-ts@3.1.1: + resolution: {integrity: sha512-I4diPp9Cq6ieSUH2wu+fDAVQO43xwtulo+fKEidHUwZPnYImbtkTjzIJYcDcJqxgmX31GVqNFURodvcgHcW0pA==} + engines: {node: '>=16.15.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + + vite-plugin-singlefile@2.3.3: + resolution: {integrity: sha512-XVnGH0QzbOa8fxRSsHdCarVN1BSBXNi7uLMQYlrGRN5apdHkk62XQWRJhVever0lnfuyBkwn+kvVChdm/OoOUg==} + engines: {node: '>18.0.0'} + peerDependencies: + rollup: ^4.59.0 + vite: ^5.4.21 || ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + rollup: + optional: true + + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + webrtc-adapter@9.0.5: + resolution: {integrity: sha512-U9vjByy/sK2OMXu5mmfuZFKTMIUQe34c0JXRO+oDrxJTsntdYT2iIFwYMOV7HhMTuktcZLGf2W1N/OcSf9ssWg==} + engines: {node: '>=6.0.0', npm: '>=3.10.0'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.0': {} + + '@babel/core@7.29.0(supports-color@7.2.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0(supports-color@7.2.0))(supports-color@7.2.0) + '@babel/helpers': 7.28.6 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0(supports-color@7.2.0) + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3(supports-color@7.2.0) + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.0 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6(supports-color@7.2.0)': + dependencies: + '@babel/traverse': 7.29.0(supports-color@7.2.0) + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0(supports-color@7.2.0))(supports-color@7.2.0)': + dependencies: + '@babel/core': 7.29.0(supports-color@7.2.0) + '@babel/helper-module-imports': 7.28.6(supports-color@7.2.0) + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.28.6': {} + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.28.6': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.0': + dependencies: + '@babel/types': 7.29.0 + + '@babel/plugin-transform-react-jsx-self@7.27.1(@babel/core@7.29.0(supports-color@7.2.0))': + dependencies: + '@babel/core': 7.29.0(supports-color@7.2.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/plugin-transform-react-jsx-source@7.27.1(@babel/core@7.29.0(supports-color@7.2.0))': + dependencies: + '@babel/core': 7.29.0(supports-color@7.2.0) + '@babel/helper-plugin-utils': 7.28.6 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0(supports-color@7.2.0)': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.0 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3(supports-color@7.2.0) + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@bufbuild/protobuf@1.10.1': {} + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(supports-color@7.2.0))': + dependencies: + eslint: 9.39.4(supports-color@7.2.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2(supports-color@7.2.0)': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5(supports-color@7.2.0)': + dependencies: + ajv: 6.14.0 + debug: 4.4.3(supports-color@7.2.0) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@floating-ui/core@1.7.5': + dependencies: + '@floating-ui/utils': 0.2.11 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 + + '@floating-ui/utils@0.2.11': {} + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@livekit/components-core@0.12.13(livekit-client@2.20.0(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1)': + dependencies: + '@floating-ui/dom': 1.7.4 + livekit-client: 2.20.0(@types/dom-mediacapture-record@1.0.22) + loglevel: 1.9.1 + rxjs: 7.8.2 + tslib: 2.8.1 + + '@livekit/components-react@2.9.21(livekit-client@2.20.0(@types/dom-mediacapture-record@1.0.22))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(tslib@2.8.1)': + dependencies: + '@livekit/components-core': 0.12.13(livekit-client@2.20.0(@types/dom-mediacapture-record@1.0.22))(tslib@2.8.1) + clsx: 2.1.1 + events: 3.3.0 + jose: 6.2.3 + livekit-client: 2.20.0(@types/dom-mediacapture-record@1.0.22) + react: 19.2.5 + react-dom: 19.2.5(react@19.2.5) + tslib: 2.8.1 + usehooks-ts: 3.1.1(react@19.2.5) + + '@livekit/components-styles@1.2.0': {} + + '@livekit/mutex@1.1.1': {} + + '@livekit/protocol@1.46.6': + dependencies: + '@bufbuild/protobuf': 1.10.1 + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.62.0': + optional: true + + '@rollup/rollup-android-arm64@4.62.0': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.0': + optional: true + + '@rollup/rollup-darwin-x64@4.62.0': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.0': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.0': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.0': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.0': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.0': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.0': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.0': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.0': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.0': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.0': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.0': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.0': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.0': + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.0 + '@babel/types': 7.29.0 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.0 + + '@types/dom-mediacapture-record@1.0.22': {} + + '@types/estree@1.0.8': {} + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/type-utils': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 + eslint: 9.39.4(supports-color@7.2.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.4(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.2(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + debug: 4.4.3(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + + '@typescript-eslint/tsconfig-utils@8.59.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + debug: 4.4.3(supports-color@7.2.0) + eslint: 9.39.4(supports-color@7.2.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.2': {} + + '@typescript-eslint/typescript-estree@8.59.2(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.2(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.2(typescript@5.9.3) + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/visitor-keys': 8.59.2 + debug: 4.4.3(supports-color@7.2.0) + minimatch: 10.2.5 + semver: 7.8.0 + tinyglobby: 0.2.17 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(supports-color@7.2.0)) + '@typescript-eslint/scope-manager': 8.59.2 + '@typescript-eslint/types': 8.59.2 + '@typescript-eslint/typescript-estree': 8.59.2(supports-color@7.2.0)(typescript@5.9.3) + eslint: 9.39.4(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.2': + dependencies: + '@typescript-eslint/types': 8.59.2 + eslint-visitor-keys: 5.0.1 + + '@vitejs/plugin-react@4.7.0(supports-color@7.2.0)(vite@6.4.3)': + dependencies: + '@babel/core': 7.29.0(supports-color@7.2.0) + '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0(supports-color@7.2.0)) + '@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.29.0(supports-color@7.2.0)) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.3 + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.14.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.0: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.1: + dependencies: + baseline-browser-mapping: 2.10.0 + caniuse-lite: 1.0.30001774 + electron-to-chromium: 1.5.302 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001774: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + debug@4.4.3(supports-color@7.2.0): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 7.2.0 + + deep-is@0.1.4: {} + + electron-to-chromium@1.5.302: {} + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-plugin-react-hooks@5.2.0(eslint@9.39.4(supports-color@7.2.0)): + dependencies: + eslint: 9.39.4(supports-color@7.2.0) + + eslint-plugin-react-refresh@0.5.2(eslint@9.39.4(supports-color@7.2.0)): + dependencies: + eslint: 9.39.4(supports-color@7.2.0) + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(supports-color@7.2.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(supports-color@7.2.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2(supports-color@7.2.0) + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5(supports-color@7.2.0) + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.14.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3(supports-color@7.2.0) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + events@3.3.0: {} + + fast-deep-equal@3.1.3: {} + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@17.6.0: {} + + has-flag@4.0.0: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + is-extglob@2.1.1: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-number@7.0.0: {} + + isexe@2.0.0: {} + + jose@6.2.3: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@2.2.3: {} + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + livekit-client@2.20.0(@types/dom-mediacapture-record@1.0.22): + dependencies: + '@livekit/mutex': 1.1.1 + '@livekit/protocol': 1.46.6 + '@types/dom-mediacapture-record': 1.0.22 + events: 3.3.0 + jose: 6.2.3 + loglevel: 1.9.2 + sdp-transform: 2.15.0 + tslib: 2.8.1 + typed-emitter: 2.1.0 + webrtc-adapter: 9.0.5 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.debounce@4.0.8: {} + + lodash.merge@4.6.2: {} + + loglevel@1.9.1: {} + + loglevel@1.9.2: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + natural-compare@1.4.0: {} + + node-releases@2.0.27: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + postcss@8.5.15: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + punycode@2.3.1: {} + + react-dom@19.2.5(react@19.2.5): + dependencies: + react: 19.2.5 + scheduler: 0.27.0 + + react-refresh@0.17.0: {} + + react@19.2.5: {} + + resolve-from@4.0.0: {} + + rollup@4.62.0: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.0 + '@rollup/rollup-android-arm64': 4.62.0 + '@rollup/rollup-darwin-arm64': 4.62.0 + '@rollup/rollup-darwin-x64': 4.62.0 + '@rollup/rollup-freebsd-arm64': 4.62.0 + '@rollup/rollup-freebsd-x64': 4.62.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.0 + '@rollup/rollup-linux-arm-musleabihf': 4.62.0 + '@rollup/rollup-linux-arm64-gnu': 4.62.0 + '@rollup/rollup-linux-arm64-musl': 4.62.0 + '@rollup/rollup-linux-loong64-gnu': 4.62.0 + '@rollup/rollup-linux-loong64-musl': 4.62.0 + '@rollup/rollup-linux-ppc64-gnu': 4.62.0 + '@rollup/rollup-linux-ppc64-musl': 4.62.0 + '@rollup/rollup-linux-riscv64-gnu': 4.62.0 + '@rollup/rollup-linux-riscv64-musl': 4.62.0 + '@rollup/rollup-linux-s390x-gnu': 4.62.0 + '@rollup/rollup-linux-x64-gnu': 4.62.0 + '@rollup/rollup-linux-x64-musl': 4.62.0 + '@rollup/rollup-openbsd-x64': 4.62.0 + '@rollup/rollup-openharmony-arm64': 4.62.0 + '@rollup/rollup-win32-arm64-msvc': 4.62.0 + '@rollup/rollup-win32-ia32-msvc': 4.62.0 + '@rollup/rollup-win32-x64-gnu': 4.62.0 + '@rollup/rollup-win32-x64-msvc': 4.62.0 + fsevents: 2.3.3 + + rxjs@7.8.2: + dependencies: + tslib: 2.8.1 + + scheduler@0.27.0: {} + + sdp-transform@2.15.0: {} + + sdp@3.2.2: {} + + semver@6.3.1: {} + + semver@7.8.0: {} + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + source-map-js@1.2.1: {} + + strip-json-comments@3.1.1: {} + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-emitter@2.1.0: + optionalDependencies: + rxjs: 7.8.2 + + typescript-eslint@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.2(@typescript-eslint/parser@8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3))(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.59.2(supports-color@7.2.0)(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.2(eslint@9.39.4(supports-color@7.2.0))(supports-color@7.2.0)(typescript@5.9.3) + eslint: 9.39.4(supports-color@7.2.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + usehooks-ts@3.1.1(react@19.2.5): + dependencies: + lodash.debounce: 4.0.8 + react: 19.2.5 + + vite-plugin-singlefile@2.3.3(rollup@4.62.0)(vite@6.4.3): + dependencies: + micromatch: 4.0.8 + vite: 6.4.3 + optionalDependencies: + rollup: 4.62.0 + + vite@6.4.3: + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.15 + rollup: 4.62.0 + tinyglobby: 0.2.17 + optionalDependencies: + fsevents: 2.3.3 + + webrtc-adapter@9.0.5: + dependencies: + sdp: 3.2.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} diff --git a/livekit-rtc/jupyter-html/src/App.css b/livekit-rtc/jupyter-html/src/App.css new file mode 100644 index 00000000..d030aa77 --- /dev/null +++ b/livekit-rtc/jupyter-html/src/App.css @@ -0,0 +1,135 @@ +:root { + --lk-fg: #111; + --lk-fg-secondary: #333; + --lk-fg-tertiary: #555; + + --lk-bg: #fff; + --lk-bg-secondary: #f5f5f5; + --lk-bg-tertiary: #fafafa; + + --lk-accent-fg: #fff; + --lk-accent-bg: #1f8cf9; + + --lk-danger-fg: #fff; + --lk-danger: #f91f31; + --lk-danger-text: #6d0311; + --lk-danger-bg: #fecdd4; + + --lk-success-fg: #fff; + --lk-success: #1ff968; + --lk-success-text: #036d26; + --lk-success-bg: #cdfedd; + + --lk-control-fg: var(--fg); + --lk-control-bg: var(--bg-secondary); + + --lk-connection-excellent: #06db4d; + --lk-connection-good: #f9b11f; + --lk-connection-poor: #f91f31; + + + --lk-va-bar-gap: 8px; + --lk-va-bar-width: 8px; +} + + +/* reset unwanted default styles */ + +.lk-disconnect-button { + border: unset; + color: unset; + font-weight: unset; + font-size: 0.8em; +} + +.lk-agent-control-bar { + width: 100%; + justify-content: space-between; +} + +@media screen and (max-width: 600px) { + .lk-start-audio-button { + position: unset; + top: unset; + left: unset; + transform: unset; + } +} + +.lk-start-audio-button { + color: #000; + background-color: #dadada; +} + +.app-container { + max-width: 800px; + margin: 0 auto; + padding: 10px; +} + +.content { + width: 100%; + max-width: 100%; + overflow-x: hidden; + padding: 0; + margin: 0; +} + +.header { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + height: 32px; + border-bottom: 1px solid #eee; + padding-bottom: 8px; + width: 100%; +} + +.header-left { + display: flex; + align-items: center; + gap: 10px; +} + +.header h2 { + margin: 0; + font-size: 1em; + font-weight: normal; + color: #444; +} + +.header-controls { + display: flex; + align-items: center; + gap: 15px; +} + +.connection-state, +.state-inactive { + font-size: 0.85em; + margin: 0; + padding: 0; +} + +.connection-state { + color: #0066cc; +} + +.state-inactive { + color: #999; +} + +.controls { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.agent-visualizer { + display: flex; + flex-direction: row; + width: 25%; + margin-right: 32px; + height: var(--lk-control-bar-height); +} \ No newline at end of file diff --git a/livekit-rtc/jupyter-html/src/App.tsx b/livekit-rtc/jupyter-html/src/App.tsx new file mode 100644 index 00000000..76244832 --- /dev/null +++ b/livekit-rtc/jupyter-html/src/App.tsx @@ -0,0 +1,113 @@ +import React, { useState } from 'react'; +import { + LiveKitRoom, + BarVisualizer, + useVoiceAssistant, + RoomAudioRenderer, + useConnectionState, + DisconnectButton, + useStartAudio, + VoiceAssistantControlBar, +} from '@livekit/components-react'; +import '@livekit/components-styles'; +import './App.css'; +import type { SVGProps } from 'react'; +import { ConnectionState } from 'livekit-client'; + + +export function getJoinInfo(): { url: string; token: string } { + if (import.meta.env.MODE === "development") { + const url = import.meta.env.VITE_LIVEKIT_URL; + const token = import.meta.env.VITE_LIVEKIT_TOKEN; + return { url, token }; + } + + return { url: "##livekit-url-placeholder##", token: "##livekit-token-placeholder##" }; +} + +const LeaveIcon = (props: SVGProps) => ( + + + + +); + +const ConnectedContent: React.FC<{ onDisconnect: () => void }> = ({ onDisconnect }) => { + const connectionState = useConnectionState(); + const { canPlayAudio } = useStartAudio({ props: {} }); // why do I need props..? + const { state: agentState, audioTrack } = useVoiceAssistant(); + + return ( +
+
+
+

livekit-rtc

+ + {connectionState} + +
+
+ Disconnect +
+
+
+ + + {canPlayAudio && connectionState == ConnectionState.Connected && agentState !== "connecting" &&
+ +
} +
+
+ ); +}; + +const App = () => { + const [error, setError] = useState(null); + const [isConnected, setIsConnected] = useState(true); + const joinInfo = getJoinInfo(); + + if (error) return
Error: {error}
; + + if (!isConnected) { + return ( +
+
+
+

livekit-rtc

+ disconnected +
+
+ Re-run the cell to connect +
+
+
+ ); + } + + return ( + setError(err.message)} + > + setIsConnected(false)} /> + + + ); +}; +export default App; \ No newline at end of file diff --git a/livekit-rtc/jupyter-html/src/main.tsx b/livekit-rtc/jupyter-html/src/main.tsx new file mode 100644 index 00000000..4aff0256 --- /dev/null +++ b/livekit-rtc/jupyter-html/src/main.tsx @@ -0,0 +1,9 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/livekit-rtc/jupyter-html/src/vite-env.d.ts b/livekit-rtc/jupyter-html/src/vite-env.d.ts new file mode 100644 index 00000000..11f02fe2 --- /dev/null +++ b/livekit-rtc/jupyter-html/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/livekit-rtc/jupyter-html/tsconfig.app.json b/livekit-rtc/jupyter-html/tsconfig.app.json new file mode 100644 index 00000000..358ca9ba --- /dev/null +++ b/livekit-rtc/jupyter-html/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/livekit-rtc/jupyter-html/tsconfig.json b/livekit-rtc/jupyter-html/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/livekit-rtc/jupyter-html/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/livekit-rtc/jupyter-html/tsconfig.node.json b/livekit-rtc/jupyter-html/tsconfig.node.json new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ b/livekit-rtc/jupyter-html/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/livekit-rtc/jupyter-html/vite.config.ts b/livekit-rtc/jupyter-html/vite.config.ts new file mode 100644 index 00000000..3b4cae11 --- /dev/null +++ b/livekit-rtc/jupyter-html/vite.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import { viteSingleFile } from 'vite-plugin-singlefile'; + +export default defineConfig({ + plugins: [ + react(), + viteSingleFile() // This plugin inlines all JS and CSS into index.html + ], +}); + diff --git a/livekit-rtc/livekit/rtc/__init__.py b/livekit-rtc/livekit/rtc/__init__.py index c04d96a0..fabc1a5f 100644 --- a/livekit-rtc/livekit/rtc/__init__.py +++ b/livekit-rtc/livekit/rtc/__init__.py @@ -12,25 +12,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -"""LiveKit RTC SDK""" +"""LiveKit SDK for Python +`pip install livekit` + +See https://docs.livekit.io/home/client/connect/#installing-the-livekit-sdk for more information. +""" from ._proto import stats_pb2 as stats -from ._proto.e2ee_pb2 import EncryptionState, EncryptionType +from ._proto.e2ee_pb2 import EncryptionState, EncryptionType, KeyDerivationFunction +from ._proto.participant_pb2 import ( + ParticipantKind, + ParticipantKindDetail, + ParticipantState, + DisconnectReason, +) from ._proto.room_pb2 import ( - ConnectionQuality, ConnectionState, ContinualGatheringPolicy, DataPacketKind, IceServer, IceTransportType, + SimulateScenarioKind, TrackPublishOptions, + VideoEncoding, +) +from ._proto.track_pb2 import ( + FrameMetadataFeature, + StreamState, + TrackKind, + TrackSource, + ParticipantTrackPermission, ) -from ._proto.track_pb2 import StreamState, TrackKind, TrackSource -from ._proto.video_frame_pb2 import VideoBufferType, VideoRotation +from ._proto.video_frame_pb2 import FrameMetadata, VideoBufferType, VideoCodec, VideoRotation +from ._proto.track_publication_pb2 import VideoQuality from .audio_frame import AudioFrame from .audio_source import AudioSource -from .audio_stream import AudioFrameEvent, AudioStream -from .chat import ChatManager, ChatMessage +from .audio_stream import AudioFrameEvent, AudioStream, NoiseCancellationOptions +from .audio_filter import AudioFilter from .e2ee import ( E2EEManager, E2EEOptions, @@ -38,8 +56,22 @@ KeyProvider, KeyProviderOptions, ) -from .participant import LocalParticipant, Participant, RemoteParticipant -from .room import ConnectError, DataPacket, Room, RoomOptions, RtcConfiguration, SipDTMF +from .participant import ( + ConnectionQuality, + DegradationPreference, + LocalParticipant, + Participant, + RemoteParticipant, +) +from .room import ( + ConnectError, + DataPacket, + Room, + RoomOptions, + RtcConfiguration, + SipDTMF, + RtcStats, +) from .track import ( AudioTrack, LocalAudioTrack, @@ -51,6 +83,7 @@ Track, VideoTrack, ) +from .event_emitter import EventEmitter from .track_publication import ( LocalTrackPublication, RemoteTrackPublication, @@ -63,35 +96,86 @@ ) from .video_source import VideoSource from .video_stream import VideoFrameEvent, VideoStream +from .audio_resampler import AudioResampler, AudioResamplerQuality +from .audio_mixer import AudioMixer +from .apm import AudioProcessingModule +from .platform_audio import ( + PlatformAudio, + PlatformAudioSource, + PlatformAudioOptions, + PlatformAudioError, + AudioDeviceInfo, +) + +try: + from .media_devices import MediaDevices as MediaDevices + + _HAS_MEDIA_DEVICES = True +except Exception: # pragma: no cover - optional dependency (sounddevice) + _HAS_MEDIA_DEVICES = False +from .utils import combine_audio_frames +from .rpc import RpcError, RpcInvocationData +from .synchronizer import AVSynchronizer +from .data_stream import ( + TextStreamInfo, + ByteStreamInfo, + TextStreamReader, + TextStreamWriter, + ByteStreamWriter, + ByteStreamReader, +) +from .data_track import ( + LocalDataTrack, + RemoteDataTrack, + DataTrackStream, + DataTrackFrame, + DataTrackInfo, + PushFrameError, + SubscribeDataTrackError, +) +from .frame_processor import FrameProcessor __all__ = [ "ConnectionQuality", "ConnectionState", "DataPacketKind", + "DegradationPreference", "TrackPublishOptions", "IceTransportType", "ContinualGatheringPolicy", "IceServer", "EncryptionType", "EncryptionState", + "KeyDerivationFunction", "StreamState", "TrackKind", "TrackSource", + "FrameMetadataFeature", + "ParticipantTrackPermission", "VideoBufferType", "VideoRotation", + "FrameMetadata", "stats", "AudioFrame", "AudioSource", "AudioStream", + "NoiseCancellationOptions", + "AudioFilter", "AudioFrameEvent", "LocalParticipant", "Participant", + "ParticipantKind", + "ParticipantKindDetail", + "ParticipantState", + "DisconnectReason", "RemoteParticipant", "ConnectError", "Room", "RoomOptions", "RtcConfiguration", + "SimulateScenarioKind", "SipDTMF", + "RtcStats", "DataPacket", "LocalAudioTrack", "LocalVideoTrack", @@ -112,11 +196,44 @@ "TrackPublication", "Transcription", "TranscriptionSegment", + "VideoCodec", + "VideoEncoding", + "VideoQuality", "VideoFrame", + "VideoFrameEvent", "VideoSource", "VideoStream", - "VideoFrameEvent", - "ChatManager", - "ChatMessage", + "AudioMixer", + "AudioResampler", + "AudioResamplerQuality", + "RpcError", + "RpcInvocationData", + "EventEmitter", + "combine_audio_frames", + "AVSynchronizer", + "TextStreamInfo", + "ByteStreamInfo", + "TextStreamReader", + "TextStreamWriter", + "ByteStreamReader", + "ByteStreamWriter", + "AudioProcessingModule", + "PlatformAudio", + "PlatformAudioSource", + "PlatformAudioOptions", + "PlatformAudioError", + "AudioDeviceInfo", + "FrameProcessor", + "LocalDataTrack", + "RemoteDataTrack", + "DataTrackStream", + "DataTrackFrame", + "DataTrackInfo", + "PushFrameError", + "SubscribeDataTrackError", "__version__", ] + +# add MediaDevices if available +if _HAS_MEDIA_DEVICES: + __all__.append("MediaDevices") diff --git a/livekit-rtc/livekit/rtc/_event_emitter.py b/livekit-rtc/livekit/rtc/_event_emitter.py deleted file mode 100644 index 3c534bbe..00000000 --- a/livekit-rtc/livekit/rtc/_event_emitter.py +++ /dev/null @@ -1,48 +0,0 @@ -from typing import Callable, Dict, Set, Optional, Generic, TypeVar - -T = TypeVar("T") - - -class EventEmitter(Generic[T]): - def __init__(self) -> None: - self._events: Dict[T, Set[Callable]] = dict() - - def emit(self, event: T, *args, **kwargs) -> None: - if event in self._events: - callables = self._events[event].copy() - for callback in callables: - callback(*args, **kwargs) - - def once(self, event: T, callback: Optional[Callable] = None) -> Callable: - if callback is not None: - - def once_callback(*args, **kwargs): - self.off(event, once_callback) - callback(*args, **kwargs) - - return self.on(event, once_callback) - else: - - def decorator(callback: Callable) -> Callable: - self.once(event, callback) - return callback - - return decorator - - def on(self, event: T, callback: Optional[Callable] = None) -> Callable: - if callback is not None: - if event not in self._events: - self._events[event] = set() - self._events[event].add(callback) - return callback - else: - - def decorator(callback: Callable) -> Callable: - self.on(event, callback) - return callback - - return decorator - - def off(self, event: T, callback: Callable) -> None: - if event in self._events: - self._events[event].remove(callback) diff --git a/livekit-rtc/livekit/rtc/_ffi_client.py b/livekit-rtc/livekit/rtc/_ffi_client.py index 9412bfba..81ed71d3 100644 --- a/livekit-rtc/livekit/rtc/_ffi_client.py +++ b/livekit-rtc/livekit/rtc/_ffi_client.py @@ -14,41 +14,47 @@ import signal import asyncio +import sys from contextlib import ExitStack import ctypes import importlib.resources +from .version import __version__ import logging import os import platform import atexit import threading -from typing import Generic, List, Optional, TypeVar +from typing import Callable, Generic, List, Optional, TypeVar from ._proto import ffi_pb2 as proto_ffi from ._utils import Queue, classproperty - -logger = logging.getLogger("livekit") +from .log import logger _resource_files = ExitStack() atexit.register(_resource_files.close) -def get_ffi_lib(): +def _lib_name() -> Optional[str]: + if platform.system() == "Linux": + return "liblivekit_ffi.so" + elif platform.system() == "Darwin": + return "liblivekit_ffi.dylib" + elif platform.system() == "Windows": + return "livekit_ffi.dll" + return None + + +def get_ffi_lib() -> ctypes.CDLL: # allow to override the lib path using an env var libpath = os.environ.get("LIVEKIT_LIB_PATH", "").strip() if libpath: return ctypes.CDLL(libpath) - if platform.system() == "Linux": - libname = "liblivekit_ffi.so" - elif platform.system() == "Darwin": - libname = "liblivekit_ffi.dylib" - elif platform.system() == "Windows": - libname = "livekit_ffi.dll" - else: + libname = _lib_name() + if libname is None: raise Exception( - f"no ffi library found for platform {platform.system()}. \ - Set LIVEKIT_LIB_PATH to specify a the lib path" + f"no ffi library found for platform {platform.system()}. " + "Set LIVEKIT_LIB_PATH to specify the lib path" ) res = importlib.resources.files("livekit.rtc.resources") / libname @@ -57,23 +63,8 @@ def get_ffi_lib(): return ctypes.CDLL(str(path)) -ffi_lib = get_ffi_lib() ffi_cb_fnc = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t) -# C function types -ffi_lib.livekit_ffi_initialize.argtypes = [ffi_cb_fnc, ctypes.c_bool] - -ffi_lib.livekit_ffi_request.argtypes = [ - ctypes.POINTER(ctypes.c_ubyte), - ctypes.c_size_t, - ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), - ctypes.POINTER(ctypes.c_size_t), -] -ffi_lib.livekit_ffi_request.restype = ctypes.c_uint64 - -ffi_lib.livekit_ffi_drop_handle.argtypes = [ctypes.c_uint64] -ffi_lib.livekit_ffi_drop_handle.restype = ctypes.c_bool - INVALID_HANDLE = 0 @@ -82,7 +73,7 @@ def __init__(self, handle: int) -> None: self.handle = handle self._disposed = False - def __del__(self): + def __del__(self) -> None: self.dispose() @property @@ -92,7 +83,14 @@ def disposed(self) -> bool: def dispose(self) -> None: if self.handle != INVALID_HANDLE and not self._disposed: self._disposed = True - assert ffi_lib.livekit_ffi_drop_handle(ctypes.c_uint64(self.handle)) + ffi = FfiClient._instance + if ffi is None or ffi._pid != os.getpid(): + return + dropped = ffi._ffi_lib.livekit_ffi_drop_handle(ctypes.c_uint64(self.handle)) + assert dropped + + def __repr__(self) -> str: + return f"FfiHandle({self.handle})" T = TypeVar("T") @@ -101,35 +99,66 @@ def dispose(self) -> None: class FfiQueue(Generic[T]): def __init__(self) -> None: self._lock = threading.RLock() - self._subscribers: List[tuple[Queue[T], asyncio.AbstractEventLoop]] = [] + # Format: (queue, loop, filter_fn or None) + self._subscribers: List[ + tuple[Queue[T], asyncio.AbstractEventLoop, Optional[Callable[[T], bool]]] + ] = [] def put(self, item: T) -> None: with self._lock: - for queue, loop in self._subscribers: - loop.call_soon_threadsafe(queue.put_nowait, item) - - def subscribe(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> Queue[T]: + for queue, loop, filter_fn in self._subscribers: + # If filter provided, skip items that don't match + if filter_fn is not None: + try: + if not filter_fn(item): + continue + except Exception: + pass # On filter error, deliver the item + + try: + loop.call_soon_threadsafe(queue.put_nowait, item) + except Exception as e: + # this could happen if user closes the runloop without unsubscribing first + # it's not good when it does occur, but we should not fail the entire runloop + logger.error("error putting to queue: %s", e) + + def subscribe( + self, + loop: Optional[asyncio.AbstractEventLoop] = None, + filter_fn: Optional[Callable[[T], bool]] = None, + ) -> Queue[T]: + """Subscribe to FFI events. + + Args: + loop: Event loop to use (defaults to current). + filter_fn: Optional filter function. If provided, only items where + filter_fn(item) returns True will be delivered. + If None, receives all events (original behavior). + + Returns: + Queue to receive events from. + """ with self._lock: queue = Queue[T]() loop = loop or asyncio.get_event_loop() - self._subscribers.append((queue, loop)) + self._subscribers.append((queue, loop, filter_fn)) return queue def unsubscribe(self, queue: Queue[T]) -> None: with self._lock: # looping here is ok, since we don't expect a lot of subscribers - for i, (q, _) in enumerate(self._subscribers): + for i, (q, _, _) in enumerate(self._subscribers): if q == queue: self._subscribers.pop(i) break -@ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t) +@ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_uint8), ctypes.c_size_t) # type: ignore[untyped-decorator] def ffi_event_callback( data_ptr: ctypes.POINTER(ctypes.c_uint8), # type: ignore data_len: ctypes.c_size_t, ) -> None: - event_data = bytes(data_ptr[: int(data_len)]) + event_data = ctypes.string_at(data_ptr, int(data_len)) event = proto_ffi.FfiEvent() event.ParseFromString(event_data) @@ -157,7 +186,7 @@ def ffi_event_callback( return # no need to queue the logs elif which == "panic": - logger.critical("Panic: %s", event.panic.message) + print("FFI Panic: ", event.panic.message, file=sys.stderr, flush=True) # We are in a unrecoverable state, terminate the process os.kill(os.getpid(), signal.SIGTERM) return @@ -187,36 +216,80 @@ class FfiClient: _instance: Optional["FfiClient"] = None @classproperty - def instance(self): - if self._instance is None: - self._instance = FfiClient() - return self._instance + def instance(cls) -> "FfiClient": + if cls._instance is None: + cls._instance = FfiClient() + return cls._instance def __init__(self) -> None: + self._pid = os.getpid() self._lock = threading.RLock() self._queue = FfiQueue[proto_ffi.FfiEvent]() - ffi_lib.livekit_ffi_initialize(ffi_event_callback, True) + try: + self._ffi_lib = get_ffi_lib() + except Exception as e: + libname = _lib_name() or "livekit_ffi" + raise ImportError( + "failed to load %s: %s\n" + "Install the livekit package with: pip install livekit\n" + "Or set LIVEKIT_LIB_PATH to the path of the native library." % (libname, e) + ) from None + self._ffi_lib.livekit_ffi_initialize.argtypes = [ + ffi_cb_fnc, + ctypes.c_bool, + ctypes.c_char_p, + ctypes.c_char_p, + ] + self._ffi_lib.livekit_ffi_request.argtypes = [ + ctypes.POINTER(ctypes.c_ubyte), + ctypes.c_size_t, + ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte)), + ctypes.POINTER(ctypes.c_size_t), + ] + self._ffi_lib.livekit_ffi_request.restype = ctypes.c_uint64 + self._ffi_lib.livekit_ffi_drop_handle.argtypes = [ctypes.c_uint64] + self._ffi_lib.livekit_ffi_drop_handle.restype = ctypes.c_bool + self._ffi_lib.livekit_ffi_dispose.argtypes = [] + self._ffi_lib.livekit_ffi_dispose.restype = None + + self._ffi_lib.livekit_ffi_initialize( + ffi_event_callback, True, b"python", __version__.encode("ascii") + ) + + ffi_lib = self._ffi_lib + init_pid = self._pid + + @atexit.register + def _dispose_lk_ffi() -> None: + if os.getpid() != init_pid: + return + ffi_lib.livekit_ffi_dispose() @property def queue(self) -> FfiQueue[proto_ffi.FfiEvent]: return self._queue def request(self, req: proto_ffi.FfiRequest) -> proto_ffi.FfiResponse: + if self._pid != os.getpid(): + raise RuntimeError( + "livekit.rtc was used in a parent process before fork(); the native " + "runtime cannot be used across fork(). Do not create or connect a Room " + "(or use any livekit.rtc object) before forking child processes." + ) proto_data = req.SerializeToString() proto_len = len(proto_data) data = (ctypes.c_ubyte * proto_len)(*proto_data) resp_ptr = ctypes.POINTER(ctypes.c_ubyte)() resp_len = ctypes.c_size_t() - handle = ffi_lib.livekit_ffi_request( + handle = self._ffi_lib.livekit_ffi_request( data, proto_len, ctypes.byref(resp_ptr), ctypes.byref(resp_len) ) assert handle != INVALID_HANDLE - resp_data = bytes(resp_ptr[: resp_len.value]) + resp_data = ctypes.string_at(resp_ptr, resp_len.value) resp = proto_ffi.FfiResponse() resp.ParseFromString(resp_data) - - FfiHandle(handle) + FfiHandle(handle).dispose() return resp diff --git a/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.py b/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.py index 7bf9939a..b033cb80 100644 --- a/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: audio_frame.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,9 +13,10 @@ from . import handle_pb2 as handle__pb2 +from . import track_pb2 as track__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x61udio_frame.proto\x12\rlivekit.proto\x1a\x0chandle.proto\"[\n\x15NewAudioStreamRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x01(\x04\x12,\n\x04type\x18\x02 \x01(\x0e\x32\x1e.livekit.proto.AudioStreamType\"I\n\x16NewAudioStreamResponse\x12/\n\x06stream\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedAudioStream\"\xb5\x01\n\x15NewAudioSourceRequest\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.AudioSourceType\x12\x37\n\x07options\x18\x02 \x01(\x0b\x32!.livekit.proto.AudioSourceOptionsH\x00\x88\x01\x01\x12\x13\n\x0bsample_rate\x18\x03 \x01(\r\x12\x14\n\x0cnum_channels\x18\x04 \x01(\rB\n\n\x08_options\"I\n\x16NewAudioSourceResponse\x12/\n\x06source\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedAudioSource\"f\n\x18\x43\x61ptureAudioFrameRequest\x12\x15\n\rsource_handle\x18\x01 \x01(\x04\x12\x33\n\x06\x62uffer\x18\x02 \x01(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\"-\n\x19\x43\x61ptureAudioFrameResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"K\n\x19\x43\x61ptureAudioFrameCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\x1a\n\x18NewAudioResamplerRequest\"R\n\x19NewAudioResamplerResponse\x12\x35\n\tresampler\x18\x01 \x01(\x0b\x32\".livekit.proto.OwnedAudioResampler\"\x93\x01\n\x17RemixAndResampleRequest\x12\x18\n\x10resampler_handle\x18\x01 \x01(\x04\x12\x33\n\x06\x62uffer\x18\x02 \x01(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\x12\x14\n\x0cnum_channels\x18\x03 \x01(\r\x12\x13\n\x0bsample_rate\x18\x04 \x01(\r\"P\n\x18RemixAndResampleResponse\x12\x34\n\x06\x62uffer\x18\x01 \x01(\x0b\x32$.livekit.proto.OwnedAudioFrameBuffer\"p\n\x14\x41udioFrameBufferInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x01(\x04\x12\x14\n\x0cnum_channels\x18\x02 \x01(\r\x12\x13\n\x0bsample_rate\x18\x03 \x01(\r\x12\x1b\n\x13samples_per_channel\x18\x04 \x01(\r\"y\n\x15OwnedAudioFrameBuffer\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\x31\n\x04info\x18\x02 \x01(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\"?\n\x0f\x41udioStreamInfo\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.AudioStreamType\"o\n\x10OwnedAudioStream\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.AudioStreamInfo\"\x9f\x01\n\x10\x41udioStreamEvent\x12\x15\n\rstream_handle\x18\x01 \x01(\x04\x12;\n\x0e\x66rame_received\x18\x02 \x01(\x0b\x32!.livekit.proto.AudioFrameReceivedH\x00\x12,\n\x03\x65os\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.AudioStreamEOSH\x00\x42\t\n\x07message\"I\n\x12\x41udioFrameReceived\x12\x33\n\x05\x66rame\x18\x01 \x01(\x0b\x32$.livekit.proto.OwnedAudioFrameBuffer\"\x10\n\x0e\x41udioStreamEOS\"e\n\x12\x41udioSourceOptions\x12\x19\n\x11\x65\x63ho_cancellation\x18\x01 \x01(\x08\x12\x19\n\x11noise_suppression\x18\x02 \x01(\x08\x12\x19\n\x11\x61uto_gain_control\x18\x03 \x01(\x08\"?\n\x0f\x41udioSourceInfo\x12,\n\x04type\x18\x02 \x01(\x0e\x32\x1e.livekit.proto.AudioSourceType\"o\n\x10OwnedAudioSource\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.AudioSourceInfo\"\x14\n\x12\x41udioResamplerInfo\"u\n\x13OwnedAudioResampler\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12/\n\x04info\x18\x02 \x01(\x0b\x32!.livekit.proto.AudioResamplerInfo*A\n\x0f\x41udioStreamType\x12\x17\n\x13\x41UDIO_STREAM_NATIVE\x10\x00\x12\x15\n\x11\x41UDIO_STREAM_HTML\x10\x01**\n\x0f\x41udioSourceType\x12\x17\n\x13\x41UDIO_SOURCE_NATIVE\x10\x00\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x61udio_frame.proto\x12\rlivekit.proto\x1a\x0chandle.proto\x1a\x0btrack.proto\"\xf6\x01\n\x15NewAudioStreamRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12,\n\x04type\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.AudioStreamType\x12\x13\n\x0bsample_rate\x18\x03 \x01(\r\x12\x14\n\x0cnum_channels\x18\x04 \x01(\r\x12\x1e\n\x16\x61udio_filter_module_id\x18\x05 \x01(\t\x12\x1c\n\x14\x61udio_filter_options\x18\x06 \x01(\t\x12\x15\n\rframe_size_ms\x18\x07 \x01(\r\x12\x19\n\x11queue_size_frames\x18\x08 \x01(\r\"I\n\x16NewAudioStreamResponse\x12/\n\x06stream\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedAudioStream\"\xba\x02\n!AudioStreamFromParticipantRequest\x12\x1a\n\x12participant_handle\x18\x01 \x02(\x04\x12,\n\x04type\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.AudioStreamType\x12\x30\n\x0ctrack_source\x18\x03 \x01(\x0e\x32\x1a.livekit.proto.TrackSource\x12\x13\n\x0bsample_rate\x18\x05 \x01(\r\x12\x14\n\x0cnum_channels\x18\x06 \x01(\r\x12\x1e\n\x16\x61udio_filter_module_id\x18\x07 \x01(\t\x12\x1c\n\x14\x61udio_filter_options\x18\x08 \x01(\t\x12\x15\n\rframe_size_ms\x18\t \x01(\r\x12\x19\n\x11queue_size_frames\x18\n \x01(\r\"U\n\"AudioStreamFromParticipantResponse\x12/\n\x06stream\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedAudioStream\"\xda\x01\n\x15NewAudioSourceRequest\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.AudioSourceType\x12\x32\n\x07options\x18\x02 \x01(\x0b\x32!.livekit.proto.AudioSourceOptions\x12\x13\n\x0bsample_rate\x18\x03 \x01(\r\x12\x14\n\x0cnum_channels\x18\x04 \x01(\r\x12\x15\n\rqueue_size_ms\x18\x05 \x01(\r\x12\x1d\n\x15platform_audio_handle\x18\x06 \x01(\x04\"I\n\x16NewAudioSourceResponse\x12/\n\x06source\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedAudioSource\"\x80\x01\n\x18\x43\x61ptureAudioFrameRequest\x12\x15\n\rsource_handle\x18\x01 \x02(\x04\x12\x33\n\x06\x62uffer\x18\x02 \x02(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"-\n\x19\x43\x61ptureAudioFrameResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"<\n\x19\x43\x61ptureAudioFrameCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"0\n\x17\x43learAudioBufferRequest\x12\x15\n\rsource_handle\x18\x01 \x02(\x04\"\x1a\n\x18\x43learAudioBufferResponse\"\x1a\n\x18NewAudioResamplerRequest\"R\n\x19NewAudioResamplerResponse\x12\x35\n\tresampler\x18\x01 \x02(\x0b\x32\".livekit.proto.OwnedAudioResampler\"\x93\x01\n\x17RemixAndResampleRequest\x12\x18\n\x10resampler_handle\x18\x01 \x02(\x04\x12\x33\n\x06\x62uffer\x18\x02 \x02(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\x12\x14\n\x0cnum_channels\x18\x03 \x02(\r\x12\x13\n\x0bsample_rate\x18\x04 \x02(\r\"P\n\x18RemixAndResampleResponse\x12\x34\n\x06\x62uffer\x18\x01 \x02(\x0b\x32$.livekit.proto.OwnedAudioFrameBuffer\"\x95\x01\n\rNewApmRequest\x12\x1e\n\x16\x65\x63ho_canceller_enabled\x18\x01 \x02(\x08\x12\x1f\n\x17gain_controller_enabled\x18\x02 \x02(\x08\x12 \n\x18high_pass_filter_enabled\x18\x03 \x02(\x08\x12!\n\x19noise_suppression_enabled\x18\x04 \x02(\x08\"6\n\x0eNewApmResponse\x12$\n\x03\x61pm\x18\x01 \x02(\x0b\x32\x17.livekit.proto.OwnedApm\"x\n\x17\x41pmProcessStreamRequest\x12\x12\n\napm_handle\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x61ta_ptr\x18\x02 \x02(\x04\x12\x0c\n\x04size\x18\x03 \x02(\r\x12\x13\n\x0bsample_rate\x18\x04 \x02(\r\x12\x14\n\x0cnum_channels\x18\x05 \x02(\r\")\n\x18\x41pmProcessStreamResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"\x7f\n\x1e\x41pmProcessReverseStreamRequest\x12\x12\n\napm_handle\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x61ta_ptr\x18\x02 \x02(\x04\x12\x0c\n\x04size\x18\x03 \x02(\r\x12\x13\n\x0bsample_rate\x18\x04 \x02(\r\x12\x14\n\x0cnum_channels\x18\x05 \x02(\r\"0\n\x1f\x41pmProcessReverseStreamResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"@\n\x18\x41pmSetStreamDelayRequest\x12\x12\n\napm_handle\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x65lay_ms\x18\x02 \x02(\x05\"*\n\x19\x41pmSetStreamDelayResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"\x9c\x02\n\x16NewSoxResamplerRequest\x12\x12\n\ninput_rate\x18\x01 \x02(\x01\x12\x13\n\x0boutput_rate\x18\x02 \x02(\x01\x12\x14\n\x0cnum_channels\x18\x03 \x02(\r\x12<\n\x0finput_data_type\x18\x04 \x02(\x0e\x32#.livekit.proto.SoxResamplerDataType\x12=\n\x10output_data_type\x18\x05 \x02(\x0e\x32#.livekit.proto.SoxResamplerDataType\x12\x37\n\x0equality_recipe\x18\x06 \x02(\x0e\x32\x1f.livekit.proto.SoxQualityRecipe\x12\r\n\x05\x66lags\x18\x07 \x01(\r\"l\n\x17NewSoxResamplerResponse\x12\x35\n\tresampler\x18\x01 \x01(\x0b\x32 .livekit.proto.OwnedSoxResamplerH\x00\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x42\t\n\x07message\"S\n\x17PushSoxResamplerRequest\x12\x18\n\x10resampler_handle\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x61ta_ptr\x18\x02 \x02(\x04\x12\x0c\n\x04size\x18\x03 \x02(\r\"K\n\x18PushSoxResamplerResponse\x12\x12\n\noutput_ptr\x18\x01 \x02(\x04\x12\x0c\n\x04size\x18\x02 \x02(\r\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"4\n\x18\x46lushSoxResamplerRequest\x12\x18\n\x10resampler_handle\x18\x01 \x02(\x04\"L\n\x19\x46lushSoxResamplerResponse\x12\x12\n\noutput_ptr\x18\x01 \x02(\x04\x12\x0c\n\x04size\x18\x02 \x02(\r\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"p\n\x14\x41udioFrameBufferInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x02(\x04\x12\x14\n\x0cnum_channels\x18\x02 \x02(\r\x12\x13\n\x0bsample_rate\x18\x03 \x02(\r\x12\x1b\n\x13samples_per_channel\x18\x04 \x02(\r\"y\n\x15OwnedAudioFrameBuffer\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\x31\n\x04info\x18\x02 \x02(\x0b\x32#.livekit.proto.AudioFrameBufferInfo\"?\n\x0f\x41udioStreamInfo\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.AudioStreamType\"o\n\x10OwnedAudioStream\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.AudioStreamInfo\"\x9f\x01\n\x10\x41udioStreamEvent\x12\x15\n\rstream_handle\x18\x01 \x02(\x04\x12;\n\x0e\x66rame_received\x18\x02 \x01(\x0b\x32!.livekit.proto.AudioFrameReceivedH\x00\x12,\n\x03\x65os\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.AudioStreamEOSH\x00\x42\t\n\x07message\"I\n\x12\x41udioFrameReceived\x12\x33\n\x05\x66rame\x18\x01 \x02(\x0b\x32$.livekit.proto.OwnedAudioFrameBuffer\"\x10\n\x0e\x41udioStreamEOS\"~\n\x12\x41udioSourceOptions\x12\x19\n\x11\x65\x63ho_cancellation\x18\x01 \x02(\x08\x12\x19\n\x11noise_suppression\x18\x02 \x02(\x08\x12\x19\n\x11\x61uto_gain_control\x18\x03 \x02(\x08\x12\x17\n\x0fprefer_hardware\x18\x04 \x01(\x08\"?\n\x0f\x41udioSourceInfo\x12,\n\x04type\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.AudioSourceType\"o\n\x10OwnedAudioSource\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.AudioSourceInfo\"\x14\n\x12\x41udioResamplerInfo\"u\n\x13OwnedAudioResampler\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12/\n\x04info\x18\x02 \x02(\x0b\x32!.livekit.proto.AudioResamplerInfo\"9\n\x08OwnedApm\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\"\x12\n\x10SoxResamplerInfo\"q\n\x11OwnedSoxResampler\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12-\n\x04info\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.SoxResamplerInfo\"\\\n\x1cLoadAudioFilterPluginRequest\x12\x13\n\x0bplugin_path\x18\x01 \x02(\t\x12\x14\n\x0c\x64\x65pendencies\x18\x02 \x03(\t\x12\x11\n\tmodule_id\x18\x03 \x02(\t\".\n\x1dLoadAudioFilterPluginResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"<\n\x0f\x41udioDeviceInfo\x12\r\n\x05index\x18\x01 \x02(\r\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x0c\n\x04guid\x18\x03 \x01(\t\"Q\n\x11PlatformAudioInfo\x12\x1e\n\x16recording_device_count\x18\x01 \x02(\x05\x12\x1c\n\x14playout_device_count\x18\x02 \x02(\x05\"s\n\x12OwnedPlatformAudio\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12.\n\x04info\x18\x02 \x02(\x0b\x32 .livekit.proto.PlatformAudioInfo\"\x19\n\x17NewPlatformAudioRequest\"s\n\x18NewPlatformAudioResponse\x12;\n\x0eplatform_audio\x18\x01 \x01(\x0b\x32!.livekit.proto.OwnedPlatformAudioH\x00\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x42\t\n\x07message\"7\n\x16GetAudioDevicesRequest\x12\x1d\n\x15platform_audio_handle\x18\x01 \x02(\x04\"\x9c\x01\n\x17GetAudioDevicesResponse\x12\x37\n\x0fplayout_devices\x18\x01 \x03(\x0b\x32\x1e.livekit.proto.AudioDeviceInfo\x12\x39\n\x11recording_devices\x18\x02 \x03(\x0b\x32\x1e.livekit.proto.AudioDeviceInfo\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"M\n\x19SetRecordingDeviceRequest\x12\x1d\n\x15platform_audio_handle\x18\x01 \x02(\x04\x12\x11\n\tdevice_id\x18\x02 \x02(\t\"+\n\x1aSetRecordingDeviceResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"K\n\x17SetPlayoutDeviceRequest\x12\x1d\n\x15platform_audio_handle\x18\x01 \x02(\x04\x12\x11\n\tdevice_id\x18\x02 \x02(\t\")\n\x18SetPlayoutDeviceResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"6\n\x15StartRecordingRequest\x12\x1d\n\x15platform_audio_handle\x18\x01 \x02(\x04\"\'\n\x16StartRecordingResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"5\n\x14StopRecordingRequest\x12\x1d\n\x15platform_audio_handle\x18\x01 \x02(\x04\"&\n\x15StopRecordingResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t*J\n\x14SoxResamplerDataType\x12\x18\n\x14SOXR_DATATYPE_INT16I\x10\x00\x12\x18\n\x14SOXR_DATATYPE_INT16S\x10\x01*\x8b\x01\n\x10SoxQualityRecipe\x12\x16\n\x12SOXR_QUALITY_QUICK\x10\x00\x12\x14\n\x10SOXR_QUALITY_LOW\x10\x01\x12\x17\n\x13SOXR_QUALITY_MEDIUM\x10\x02\x12\x15\n\x11SOXR_QUALITY_HIGH\x10\x03\x12\x19\n\x15SOXR_QUALITY_VERYHIGH\x10\x04*\x97\x01\n\x0bSoxFlagBits\x12\x16\n\x12SOXR_ROLLOFF_SMALL\x10\x00\x12\x17\n\x13SOXR_ROLLOFF_MEDIUM\x10\x01\x12\x15\n\x11SOXR_ROLLOFF_NONE\x10\x02\x12\x18\n\x14SOXR_HIGH_PREC_CLOCK\x10\x03\x12\x19\n\x15SOXR_DOUBLE_PRECISION\x10\x04\x12\x0b\n\x07SOXR_VR\x10\x05*A\n\x0f\x41udioStreamType\x12\x17\n\x13\x41UDIO_STREAM_NATIVE\x10\x00\x12\x15\n\x11\x41UDIO_STREAM_HTML\x10\x01*E\n\x0f\x41udioSourceType\x12\x17\n\x13\x41UDIO_SOURCE_NATIVE\x10\x00\x12\x19\n\x15\x41UDIO_SOURCE_PLATFORM\x10\x01\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -23,54 +24,136 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_AUDIOSTREAMTYPE']._serialized_start=2141 - _globals['_AUDIOSTREAMTYPE']._serialized_end=2206 - _globals['_AUDIOSOURCETYPE']._serialized_start=2208 - _globals['_AUDIOSOURCETYPE']._serialized_end=2250 - _globals['_NEWAUDIOSTREAMREQUEST']._serialized_start=50 - _globals['_NEWAUDIOSTREAMREQUEST']._serialized_end=141 - _globals['_NEWAUDIOSTREAMRESPONSE']._serialized_start=143 - _globals['_NEWAUDIOSTREAMRESPONSE']._serialized_end=216 - _globals['_NEWAUDIOSOURCEREQUEST']._serialized_start=219 - _globals['_NEWAUDIOSOURCEREQUEST']._serialized_end=400 - _globals['_NEWAUDIOSOURCERESPONSE']._serialized_start=402 - _globals['_NEWAUDIOSOURCERESPONSE']._serialized_end=475 - _globals['_CAPTUREAUDIOFRAMEREQUEST']._serialized_start=477 - _globals['_CAPTUREAUDIOFRAMEREQUEST']._serialized_end=579 - _globals['_CAPTUREAUDIOFRAMERESPONSE']._serialized_start=581 - _globals['_CAPTUREAUDIOFRAMERESPONSE']._serialized_end=626 - _globals['_CAPTUREAUDIOFRAMECALLBACK']._serialized_start=628 - _globals['_CAPTUREAUDIOFRAMECALLBACK']._serialized_end=703 - _globals['_NEWAUDIORESAMPLERREQUEST']._serialized_start=705 - _globals['_NEWAUDIORESAMPLERREQUEST']._serialized_end=731 - _globals['_NEWAUDIORESAMPLERRESPONSE']._serialized_start=733 - _globals['_NEWAUDIORESAMPLERRESPONSE']._serialized_end=815 - _globals['_REMIXANDRESAMPLEREQUEST']._serialized_start=818 - _globals['_REMIXANDRESAMPLEREQUEST']._serialized_end=965 - _globals['_REMIXANDRESAMPLERESPONSE']._serialized_start=967 - _globals['_REMIXANDRESAMPLERESPONSE']._serialized_end=1047 - _globals['_AUDIOFRAMEBUFFERINFO']._serialized_start=1049 - _globals['_AUDIOFRAMEBUFFERINFO']._serialized_end=1161 - _globals['_OWNEDAUDIOFRAMEBUFFER']._serialized_start=1163 - _globals['_OWNEDAUDIOFRAMEBUFFER']._serialized_end=1284 - _globals['_AUDIOSTREAMINFO']._serialized_start=1286 - _globals['_AUDIOSTREAMINFO']._serialized_end=1349 - _globals['_OWNEDAUDIOSTREAM']._serialized_start=1351 - _globals['_OWNEDAUDIOSTREAM']._serialized_end=1462 - _globals['_AUDIOSTREAMEVENT']._serialized_start=1465 - _globals['_AUDIOSTREAMEVENT']._serialized_end=1624 - _globals['_AUDIOFRAMERECEIVED']._serialized_start=1626 - _globals['_AUDIOFRAMERECEIVED']._serialized_end=1699 - _globals['_AUDIOSTREAMEOS']._serialized_start=1701 - _globals['_AUDIOSTREAMEOS']._serialized_end=1717 - _globals['_AUDIOSOURCEOPTIONS']._serialized_start=1719 - _globals['_AUDIOSOURCEOPTIONS']._serialized_end=1820 - _globals['_AUDIOSOURCEINFO']._serialized_start=1822 - _globals['_AUDIOSOURCEINFO']._serialized_end=1885 - _globals['_OWNEDAUDIOSOURCE']._serialized_start=1887 - _globals['_OWNEDAUDIOSOURCE']._serialized_end=1998 - _globals['_AUDIORESAMPLERINFO']._serialized_start=2000 - _globals['_AUDIORESAMPLERINFO']._serialized_end=2020 - _globals['_OWNEDAUDIORESAMPLER']._serialized_start=2022 - _globals['_OWNEDAUDIORESAMPLER']._serialized_end=2139 + _globals['_SOXRESAMPLERDATATYPE']._serialized_start=5613 + _globals['_SOXRESAMPLERDATATYPE']._serialized_end=5687 + _globals['_SOXQUALITYRECIPE']._serialized_start=5690 + _globals['_SOXQUALITYRECIPE']._serialized_end=5829 + _globals['_SOXFLAGBITS']._serialized_start=5832 + _globals['_SOXFLAGBITS']._serialized_end=5983 + _globals['_AUDIOSTREAMTYPE']._serialized_start=5985 + _globals['_AUDIOSTREAMTYPE']._serialized_end=6050 + _globals['_AUDIOSOURCETYPE']._serialized_start=6052 + _globals['_AUDIOSOURCETYPE']._serialized_end=6121 + _globals['_NEWAUDIOSTREAMREQUEST']._serialized_start=64 + _globals['_NEWAUDIOSTREAMREQUEST']._serialized_end=310 + _globals['_NEWAUDIOSTREAMRESPONSE']._serialized_start=312 + _globals['_NEWAUDIOSTREAMRESPONSE']._serialized_end=385 + _globals['_AUDIOSTREAMFROMPARTICIPANTREQUEST']._serialized_start=388 + _globals['_AUDIOSTREAMFROMPARTICIPANTREQUEST']._serialized_end=702 + _globals['_AUDIOSTREAMFROMPARTICIPANTRESPONSE']._serialized_start=704 + _globals['_AUDIOSTREAMFROMPARTICIPANTRESPONSE']._serialized_end=789 + _globals['_NEWAUDIOSOURCEREQUEST']._serialized_start=792 + _globals['_NEWAUDIOSOURCEREQUEST']._serialized_end=1010 + _globals['_NEWAUDIOSOURCERESPONSE']._serialized_start=1012 + _globals['_NEWAUDIOSOURCERESPONSE']._serialized_end=1085 + _globals['_CAPTUREAUDIOFRAMEREQUEST']._serialized_start=1088 + _globals['_CAPTUREAUDIOFRAMEREQUEST']._serialized_end=1216 + _globals['_CAPTUREAUDIOFRAMERESPONSE']._serialized_start=1218 + _globals['_CAPTUREAUDIOFRAMERESPONSE']._serialized_end=1263 + _globals['_CAPTUREAUDIOFRAMECALLBACK']._serialized_start=1265 + _globals['_CAPTUREAUDIOFRAMECALLBACK']._serialized_end=1325 + _globals['_CLEARAUDIOBUFFERREQUEST']._serialized_start=1327 + _globals['_CLEARAUDIOBUFFERREQUEST']._serialized_end=1375 + _globals['_CLEARAUDIOBUFFERRESPONSE']._serialized_start=1377 + _globals['_CLEARAUDIOBUFFERRESPONSE']._serialized_end=1403 + _globals['_NEWAUDIORESAMPLERREQUEST']._serialized_start=1405 + _globals['_NEWAUDIORESAMPLERREQUEST']._serialized_end=1431 + _globals['_NEWAUDIORESAMPLERRESPONSE']._serialized_start=1433 + _globals['_NEWAUDIORESAMPLERRESPONSE']._serialized_end=1515 + _globals['_REMIXANDRESAMPLEREQUEST']._serialized_start=1518 + _globals['_REMIXANDRESAMPLEREQUEST']._serialized_end=1665 + _globals['_REMIXANDRESAMPLERESPONSE']._serialized_start=1667 + _globals['_REMIXANDRESAMPLERESPONSE']._serialized_end=1747 + _globals['_NEWAPMREQUEST']._serialized_start=1750 + _globals['_NEWAPMREQUEST']._serialized_end=1899 + _globals['_NEWAPMRESPONSE']._serialized_start=1901 + _globals['_NEWAPMRESPONSE']._serialized_end=1955 + _globals['_APMPROCESSSTREAMREQUEST']._serialized_start=1957 + _globals['_APMPROCESSSTREAMREQUEST']._serialized_end=2077 + _globals['_APMPROCESSSTREAMRESPONSE']._serialized_start=2079 + _globals['_APMPROCESSSTREAMRESPONSE']._serialized_end=2120 + _globals['_APMPROCESSREVERSESTREAMREQUEST']._serialized_start=2122 + _globals['_APMPROCESSREVERSESTREAMREQUEST']._serialized_end=2249 + _globals['_APMPROCESSREVERSESTREAMRESPONSE']._serialized_start=2251 + _globals['_APMPROCESSREVERSESTREAMRESPONSE']._serialized_end=2299 + _globals['_APMSETSTREAMDELAYREQUEST']._serialized_start=2301 + _globals['_APMSETSTREAMDELAYREQUEST']._serialized_end=2365 + _globals['_APMSETSTREAMDELAYRESPONSE']._serialized_start=2367 + _globals['_APMSETSTREAMDELAYRESPONSE']._serialized_end=2409 + _globals['_NEWSOXRESAMPLERREQUEST']._serialized_start=2412 + _globals['_NEWSOXRESAMPLERREQUEST']._serialized_end=2696 + _globals['_NEWSOXRESAMPLERRESPONSE']._serialized_start=2698 + _globals['_NEWSOXRESAMPLERRESPONSE']._serialized_end=2806 + _globals['_PUSHSOXRESAMPLERREQUEST']._serialized_start=2808 + _globals['_PUSHSOXRESAMPLERREQUEST']._serialized_end=2891 + _globals['_PUSHSOXRESAMPLERRESPONSE']._serialized_start=2893 + _globals['_PUSHSOXRESAMPLERRESPONSE']._serialized_end=2968 + _globals['_FLUSHSOXRESAMPLERREQUEST']._serialized_start=2970 + _globals['_FLUSHSOXRESAMPLERREQUEST']._serialized_end=3022 + _globals['_FLUSHSOXRESAMPLERRESPONSE']._serialized_start=3024 + _globals['_FLUSHSOXRESAMPLERRESPONSE']._serialized_end=3100 + _globals['_AUDIOFRAMEBUFFERINFO']._serialized_start=3102 + _globals['_AUDIOFRAMEBUFFERINFO']._serialized_end=3214 + _globals['_OWNEDAUDIOFRAMEBUFFER']._serialized_start=3216 + _globals['_OWNEDAUDIOFRAMEBUFFER']._serialized_end=3337 + _globals['_AUDIOSTREAMINFO']._serialized_start=3339 + _globals['_AUDIOSTREAMINFO']._serialized_end=3402 + _globals['_OWNEDAUDIOSTREAM']._serialized_start=3404 + _globals['_OWNEDAUDIOSTREAM']._serialized_end=3515 + _globals['_AUDIOSTREAMEVENT']._serialized_start=3518 + _globals['_AUDIOSTREAMEVENT']._serialized_end=3677 + _globals['_AUDIOFRAMERECEIVED']._serialized_start=3679 + _globals['_AUDIOFRAMERECEIVED']._serialized_end=3752 + _globals['_AUDIOSTREAMEOS']._serialized_start=3754 + _globals['_AUDIOSTREAMEOS']._serialized_end=3770 + _globals['_AUDIOSOURCEOPTIONS']._serialized_start=3772 + _globals['_AUDIOSOURCEOPTIONS']._serialized_end=3898 + _globals['_AUDIOSOURCEINFO']._serialized_start=3900 + _globals['_AUDIOSOURCEINFO']._serialized_end=3963 + _globals['_OWNEDAUDIOSOURCE']._serialized_start=3965 + _globals['_OWNEDAUDIOSOURCE']._serialized_end=4076 + _globals['_AUDIORESAMPLERINFO']._serialized_start=4078 + _globals['_AUDIORESAMPLERINFO']._serialized_end=4098 + _globals['_OWNEDAUDIORESAMPLER']._serialized_start=4100 + _globals['_OWNEDAUDIORESAMPLER']._serialized_end=4217 + _globals['_OWNEDAPM']._serialized_start=4219 + _globals['_OWNEDAPM']._serialized_end=4276 + _globals['_SOXRESAMPLERINFO']._serialized_start=4278 + _globals['_SOXRESAMPLERINFO']._serialized_end=4296 + _globals['_OWNEDSOXRESAMPLER']._serialized_start=4298 + _globals['_OWNEDSOXRESAMPLER']._serialized_end=4411 + _globals['_LOADAUDIOFILTERPLUGINREQUEST']._serialized_start=4413 + _globals['_LOADAUDIOFILTERPLUGINREQUEST']._serialized_end=4505 + _globals['_LOADAUDIOFILTERPLUGINRESPONSE']._serialized_start=4507 + _globals['_LOADAUDIOFILTERPLUGINRESPONSE']._serialized_end=4553 + _globals['_AUDIODEVICEINFO']._serialized_start=4555 + _globals['_AUDIODEVICEINFO']._serialized_end=4615 + _globals['_PLATFORMAUDIOINFO']._serialized_start=4617 + _globals['_PLATFORMAUDIOINFO']._serialized_end=4698 + _globals['_OWNEDPLATFORMAUDIO']._serialized_start=4700 + _globals['_OWNEDPLATFORMAUDIO']._serialized_end=4815 + _globals['_NEWPLATFORMAUDIOREQUEST']._serialized_start=4817 + _globals['_NEWPLATFORMAUDIOREQUEST']._serialized_end=4842 + _globals['_NEWPLATFORMAUDIORESPONSE']._serialized_start=4844 + _globals['_NEWPLATFORMAUDIORESPONSE']._serialized_end=4959 + _globals['_GETAUDIODEVICESREQUEST']._serialized_start=4961 + _globals['_GETAUDIODEVICESREQUEST']._serialized_end=5016 + _globals['_GETAUDIODEVICESRESPONSE']._serialized_start=5019 + _globals['_GETAUDIODEVICESRESPONSE']._serialized_end=5175 + _globals['_SETRECORDINGDEVICEREQUEST']._serialized_start=5177 + _globals['_SETRECORDINGDEVICEREQUEST']._serialized_end=5254 + _globals['_SETRECORDINGDEVICERESPONSE']._serialized_start=5256 + _globals['_SETRECORDINGDEVICERESPONSE']._serialized_end=5299 + _globals['_SETPLAYOUTDEVICEREQUEST']._serialized_start=5301 + _globals['_SETPLAYOUTDEVICEREQUEST']._serialized_end=5376 + _globals['_SETPLAYOUTDEVICERESPONSE']._serialized_start=5378 + _globals['_SETPLAYOUTDEVICERESPONSE']._serialized_end=5419 + _globals['_STARTRECORDINGREQUEST']._serialized_start=5421 + _globals['_STARTRECORDINGREQUEST']._serialized_end=5475 + _globals['_STARTRECORDINGRESPONSE']._serialized_start=5477 + _globals['_STARTRECORDINGRESPONSE']._serialized_end=5516 + _globals['_STOPRECORDINGREQUEST']._serialized_start=5518 + _globals['_STOPRECORDINGREQUEST']._serialized_end=5571 + _globals['_STOPRECORDINGRESPONSE']._serialized_start=5573 + _globals['_STOPRECORDINGRESPONSE']._serialized_end=5611 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.pyi index a5097d6b..3e227c7e 100644 --- a/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/audio_frame_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,12 +15,16 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins +import collections.abc import google.protobuf.descriptor +import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message from . import handle_pb2 import sys +from . import track_pb2 import typing if sys.version_info >= (3, 10): @@ -30,6 +34,79 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _SoxResamplerDataType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SoxResamplerDataTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SoxResamplerDataType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SOXR_DATATYPE_INT16I: _SoxResamplerDataType.ValueType # 0 + """TODO(theomonnom): support other datatypes (shouldn't really be needed)""" + SOXR_DATATYPE_INT16S: _SoxResamplerDataType.ValueType # 1 + +class SoxResamplerDataType(_SoxResamplerDataType, metaclass=_SoxResamplerDataTypeEnumTypeWrapper): ... + +SOXR_DATATYPE_INT16I: SoxResamplerDataType.ValueType # 0 +"""TODO(theomonnom): support other datatypes (shouldn't really be needed)""" +SOXR_DATATYPE_INT16S: SoxResamplerDataType.ValueType # 1 +global___SoxResamplerDataType = SoxResamplerDataType + +class _SoxQualityRecipe: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SoxQualityRecipeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SoxQualityRecipe.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SOXR_QUALITY_QUICK: _SoxQualityRecipe.ValueType # 0 + SOXR_QUALITY_LOW: _SoxQualityRecipe.ValueType # 1 + SOXR_QUALITY_MEDIUM: _SoxQualityRecipe.ValueType # 2 + SOXR_QUALITY_HIGH: _SoxQualityRecipe.ValueType # 3 + SOXR_QUALITY_VERYHIGH: _SoxQualityRecipe.ValueType # 4 + +class SoxQualityRecipe(_SoxQualityRecipe, metaclass=_SoxQualityRecipeEnumTypeWrapper): ... + +SOXR_QUALITY_QUICK: SoxQualityRecipe.ValueType # 0 +SOXR_QUALITY_LOW: SoxQualityRecipe.ValueType # 1 +SOXR_QUALITY_MEDIUM: SoxQualityRecipe.ValueType # 2 +SOXR_QUALITY_HIGH: SoxQualityRecipe.ValueType # 3 +SOXR_QUALITY_VERYHIGH: SoxQualityRecipe.ValueType # 4 +global___SoxQualityRecipe = SoxQualityRecipe + +class _SoxFlagBits: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SoxFlagBitsEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SoxFlagBits.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SOXR_ROLLOFF_SMALL: _SoxFlagBits.ValueType # 0 + """1 << 0""" + SOXR_ROLLOFF_MEDIUM: _SoxFlagBits.ValueType # 1 + """1 << 1""" + SOXR_ROLLOFF_NONE: _SoxFlagBits.ValueType # 2 + """1 << 2""" + SOXR_HIGH_PREC_CLOCK: _SoxFlagBits.ValueType # 3 + """1 << 3""" + SOXR_DOUBLE_PRECISION: _SoxFlagBits.ValueType # 4 + """1 << 4""" + SOXR_VR: _SoxFlagBits.ValueType # 5 + """1 << 5""" + +class SoxFlagBits(_SoxFlagBits, metaclass=_SoxFlagBitsEnumTypeWrapper): ... + +SOXR_ROLLOFF_SMALL: SoxFlagBits.ValueType # 0 +"""1 << 0""" +SOXR_ROLLOFF_MEDIUM: SoxFlagBits.ValueType # 1 +"""1 << 1""" +SOXR_ROLLOFF_NONE: SoxFlagBits.ValueType # 2 +"""1 << 2""" +SOXR_HIGH_PREC_CLOCK: SoxFlagBits.ValueType # 3 +"""1 << 3""" +SOXR_DOUBLE_PRECISION: SoxFlagBits.ValueType # 4 +"""1 << 4""" +SOXR_VR: SoxFlagBits.ValueType # 5 +"""1 << 5""" +global___SoxFlagBits = SoxFlagBits + class _AudioStreamType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -55,13 +132,23 @@ class _AudioSourceType: class _AudioSourceTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AudioSourceType.ValueType], builtins.type): DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor AUDIO_SOURCE_NATIVE: _AudioSourceType.ValueType # 0 + """Push-based audio source - manually capture frames via CaptureAudioFrameRequest""" + AUDIO_SOURCE_PLATFORM: _AudioSourceType.ValueType # 1 + """Platform ADM-based audio source - captures from microphone automatically + Requires PlatformAudio to be created first to enable ADM recording + """ class AudioSourceType(_AudioSourceType, metaclass=_AudioSourceTypeEnumTypeWrapper): ... AUDIO_SOURCE_NATIVE: AudioSourceType.ValueType # 0 +"""Push-based audio source - manually capture frames via CaptureAudioFrameRequest""" +AUDIO_SOURCE_PLATFORM: AudioSourceType.ValueType # 1 +"""Platform ADM-based audio source - captures from microphone automatically +Requires PlatformAudio to be created first to enable ADM recording +""" global___AudioSourceType = AudioSourceType -@typing_extensions.final +@typing.final class NewAudioStreamRequest(google.protobuf.message.Message): """Create a new AudioStream AudioStream is used to receive audio frames from a track @@ -71,19 +158,49 @@ class NewAudioStreamRequest(google.protobuf.message.Message): TRACK_HANDLE_FIELD_NUMBER: builtins.int TYPE_FIELD_NUMBER: builtins.int + SAMPLE_RATE_FIELD_NUMBER: builtins.int + NUM_CHANNELS_FIELD_NUMBER: builtins.int + AUDIO_FILTER_MODULE_ID_FIELD_NUMBER: builtins.int + AUDIO_FILTER_OPTIONS_FIELD_NUMBER: builtins.int + FRAME_SIZE_MS_FIELD_NUMBER: builtins.int + QUEUE_SIZE_FRAMES_FIELD_NUMBER: builtins.int track_handle: builtins.int type: global___AudioStreamType.ValueType + sample_rate: builtins.int + num_channels: builtins.int + audio_filter_module_id: builtins.str + """Unique identifier passed in LoadAudioFilterPluginRequest""" + audio_filter_options: builtins.str + frame_size_ms: builtins.int + queue_size_frames: builtins.int + """Maximum number of queued WebRTC sink frames. Each frame is typically 10 ms + of decoded PCM audio on the receive path. Omit this field to use the + default bounded queue size of 10 frames. Set it to 0 to request unbounded + buffering. + + If your application consumes both audio and video, keep the queue sizing + strategy coordinated across both streams. Using a much larger queue, or + unbounded buffering, for only one of them can increase end-to-end latency + for that stream and cause audio/video drift. + """ def __init__( self, *, - track_handle: builtins.int = ..., - type: global___AudioStreamType.ValueType = ..., + track_handle: builtins.int | None = ..., + type: global___AudioStreamType.ValueType | None = ..., + sample_rate: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + audio_filter_module_id: builtins.str | None = ..., + audio_filter_options: builtins.str | None = ..., + frame_size_ms: builtins.int | None = ..., + queue_size_frames: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["track_handle", b"track_handle", "type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["audio_filter_module_id", b"audio_filter_module_id", "audio_filter_options", b"audio_filter_options", "frame_size_ms", b"frame_size_ms", "num_channels", b"num_channels", "queue_size_frames", b"queue_size_frames", "sample_rate", b"sample_rate", "track_handle", b"track_handle", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_filter_module_id", b"audio_filter_module_id", "audio_filter_options", b"audio_filter_options", "frame_size_ms", b"frame_size_ms", "num_channels", b"num_channels", "queue_size_frames", b"queue_size_frames", "sample_rate", b"sample_rate", "track_handle", b"track_handle", "type", b"type"]) -> None: ... global___NewAudioStreamRequest = NewAudioStreamRequest -@typing_extensions.final +@typing.final class NewAudioStreamResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -95,12 +212,79 @@ class NewAudioStreamResponse(google.protobuf.message.Message): *, stream: global___OwnedAudioStream | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream", b"stream"]) -> None: ... global___NewAudioStreamResponse = NewAudioStreamResponse -@typing_extensions.final +@typing.final +class AudioStreamFromParticipantRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + TRACK_SOURCE_FIELD_NUMBER: builtins.int + SAMPLE_RATE_FIELD_NUMBER: builtins.int + NUM_CHANNELS_FIELD_NUMBER: builtins.int + AUDIO_FILTER_MODULE_ID_FIELD_NUMBER: builtins.int + AUDIO_FILTER_OPTIONS_FIELD_NUMBER: builtins.int + FRAME_SIZE_MS_FIELD_NUMBER: builtins.int + QUEUE_SIZE_FRAMES_FIELD_NUMBER: builtins.int + participant_handle: builtins.int + type: global___AudioStreamType.ValueType + track_source: track_pb2.TrackSource.ValueType + sample_rate: builtins.int + num_channels: builtins.int + audio_filter_module_id: builtins.str + audio_filter_options: builtins.str + frame_size_ms: builtins.int + queue_size_frames: builtins.int + """Maximum number of queued WebRTC sink frames. Each frame is typically 10 ms + of decoded PCM audio on the receive path. Omit this field to use the + default bounded queue size of 10 frames. Set it to 0 to request unbounded + buffering. + + If your application consumes both audio and video, keep the queue sizing + strategy coordinated across both streams. Using a much larger queue, or + unbounded buffering, for only one of them can increase end-to-end latency + for that stream and cause audio/video drift. + """ + def __init__( + self, + *, + participant_handle: builtins.int | None = ..., + type: global___AudioStreamType.ValueType | None = ..., + track_source: track_pb2.TrackSource.ValueType | None = ..., + sample_rate: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + audio_filter_module_id: builtins.str | None = ..., + audio_filter_options: builtins.str | None = ..., + frame_size_ms: builtins.int | None = ..., + queue_size_frames: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["audio_filter_module_id", b"audio_filter_module_id", "audio_filter_options", b"audio_filter_options", "frame_size_ms", b"frame_size_ms", "num_channels", b"num_channels", "participant_handle", b"participant_handle", "queue_size_frames", b"queue_size_frames", "sample_rate", b"sample_rate", "track_source", b"track_source", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_filter_module_id", b"audio_filter_module_id", "audio_filter_options", b"audio_filter_options", "frame_size_ms", b"frame_size_ms", "num_channels", b"num_channels", "participant_handle", b"participant_handle", "queue_size_frames", b"queue_size_frames", "sample_rate", b"sample_rate", "track_source", b"track_source", "type", b"type"]) -> None: ... + +global___AudioStreamFromParticipantRequest = AudioStreamFromParticipantRequest + +@typing.final +class AudioStreamFromParticipantResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_FIELD_NUMBER: builtins.int + @property + def stream(self) -> global___OwnedAudioStream: ... + def __init__( + self, + *, + stream: global___OwnedAudioStream | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream", b"stream"]) -> None: ... + +global___AudioStreamFromParticipantResponse = AudioStreamFromParticipantResponse + +@typing.final class NewAudioSourceRequest(google.protobuf.message.Message): """Create a new AudioSource""" @@ -110,26 +294,42 @@ class NewAudioSourceRequest(google.protobuf.message.Message): OPTIONS_FIELD_NUMBER: builtins.int SAMPLE_RATE_FIELD_NUMBER: builtins.int NUM_CHANNELS_FIELD_NUMBER: builtins.int + QUEUE_SIZE_MS_FIELD_NUMBER: builtins.int + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int type: global___AudioSourceType.ValueType - @property - def options(self) -> global___AudioSourceOptions: ... sample_rate: builtins.int + """Sample rate in Hz. Optional - defaults to 48000 if not specified. + For AudioSourcePlatform: ignored, ADM uses hardware native sample rate. + For AudioSourceNative with queue_size_ms=0 (fast path): ignored, frame values used directly. + """ num_channels: builtins.int + """Number of audio channels. Optional - defaults to 1 (mono) if not specified. + For AudioSourcePlatform: ignored, ADM uses hardware native channels. + For AudioSourceNative with queue_size_ms=0 (fast path): ignored, frame values used directly. + """ + queue_size_ms: builtins.int + platform_audio_handle: builtins.int + """For AudioSourcePlatform: the PlatformAudio handle to configure audio processing on. + If provided with options, audio processing will be configured on the PlatformAudio. + """ + @property + def options(self) -> global___AudioSourceOptions: ... def __init__( self, *, - type: global___AudioSourceType.ValueType = ..., + type: global___AudioSourceType.ValueType | None = ..., options: global___AudioSourceOptions | None = ..., - sample_rate: builtins.int = ..., - num_channels: builtins.int = ..., + sample_rate: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + queue_size_ms: builtins.int | None = ..., + platform_audio_handle: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_options", b"_options", "options", b"options"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_options", b"_options", "num_channels", b"num_channels", "options", b"options", "sample_rate", b"sample_rate", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_options", b"_options"]) -> typing_extensions.Literal["options"] | None: ... + def HasField(self, field_name: typing.Literal["num_channels", b"num_channels", "options", b"options", "platform_audio_handle", b"platform_audio_handle", "queue_size_ms", b"queue_size_ms", "sample_rate", b"sample_rate", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["num_channels", b"num_channels", "options", b"options", "platform_audio_handle", b"platform_audio_handle", "queue_size_ms", b"queue_size_ms", "sample_rate", b"sample_rate", "type", b"type"]) -> None: ... global___NewAudioSourceRequest = NewAudioSourceRequest -@typing_extensions.final +@typing.final class NewAudioSourceResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -141,14 +341,14 @@ class NewAudioSourceResponse(google.protobuf.message.Message): *, source: global___OwnedAudioSource | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["source", b"source"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["source", b"source"]) -> None: ... + def HasField(self, field_name: typing.Literal["source", b"source"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["source", b"source"]) -> None: ... global___NewAudioSourceResponse = NewAudioSourceResponse -@typing_extensions.final +@typing.final class CaptureAudioFrameRequest(google.protobuf.message.Message): - """Push a frame to an AudioSource + """Push a frame to an AudioSource The data provided must be available as long as the client receive the callback. """ @@ -156,21 +356,24 @@ class CaptureAudioFrameRequest(google.protobuf.message.Message): SOURCE_HANDLE_FIELD_NUMBER: builtins.int BUFFER_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int source_handle: builtins.int + request_async_id: builtins.int @property def buffer(self) -> global___AudioFrameBufferInfo: ... def __init__( self, *, - source_handle: builtins.int = ..., + source_handle: builtins.int | None = ..., buffer: global___AudioFrameBufferInfo | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer", "source_handle", b"source_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "request_async_id", b"request_async_id", "source_handle", b"source_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "request_async_id", b"request_async_id", "source_handle", b"source_handle"]) -> None: ... global___CaptureAudioFrameRequest = CaptureAudioFrameRequest -@typing_extensions.final +@typing.final class CaptureAudioFrameResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -179,13 +382,14 @@ class CaptureAudioFrameResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___CaptureAudioFrameResponse = CaptureAudioFrameResponse -@typing_extensions.final +@typing.final class CaptureAudioFrameCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -196,16 +400,41 @@ class CaptureAudioFrameCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... global___CaptureAudioFrameCallback = CaptureAudioFrameCallback -@typing_extensions.final +@typing.final +class ClearAudioBufferRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SOURCE_HANDLE_FIELD_NUMBER: builtins.int + source_handle: builtins.int + def __init__( + self, + *, + source_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["source_handle", b"source_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["source_handle", b"source_handle"]) -> None: ... + +global___ClearAudioBufferRequest = ClearAudioBufferRequest + +@typing.final +class ClearAudioBufferResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___ClearAudioBufferResponse = ClearAudioBufferResponse + +@typing.final class NewAudioResamplerRequest(google.protobuf.message.Message): """Create a new AudioResampler""" @@ -217,7 +446,7 @@ class NewAudioResamplerRequest(google.protobuf.message.Message): global___NewAudioResamplerRequest = NewAudioResamplerRequest -@typing_extensions.final +@typing.final class NewAudioResamplerResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -229,12 +458,12 @@ class NewAudioResamplerResponse(google.protobuf.message.Message): *, resampler: global___OwnedAudioResampler | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["resampler", b"resampler"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["resampler", b"resampler"]) -> None: ... + def HasField(self, field_name: typing.Literal["resampler", b"resampler"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["resampler", b"resampler"]) -> None: ... global___NewAudioResamplerResponse = NewAudioResamplerResponse -@typing_extensions.final +@typing.final class RemixAndResampleRequest(google.protobuf.message.Message): """Remix and resample an audio frame""" @@ -245,24 +474,24 @@ class RemixAndResampleRequest(google.protobuf.message.Message): NUM_CHANNELS_FIELD_NUMBER: builtins.int SAMPLE_RATE_FIELD_NUMBER: builtins.int resampler_handle: builtins.int - @property - def buffer(self) -> global___AudioFrameBufferInfo: ... num_channels: builtins.int sample_rate: builtins.int + @property + def buffer(self) -> global___AudioFrameBufferInfo: ... def __init__( self, *, - resampler_handle: builtins.int = ..., + resampler_handle: builtins.int | None = ..., buffer: global___AudioFrameBufferInfo | None = ..., - num_channels: builtins.int = ..., - sample_rate: builtins.int = ..., + num_channels: builtins.int | None = ..., + sample_rate: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer", "num_channels", b"num_channels", "resampler_handle", b"resampler_handle", "sample_rate", b"sample_rate"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "num_channels", b"num_channels", "resampler_handle", b"resampler_handle", "sample_rate", b"sample_rate"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "num_channels", b"num_channels", "resampler_handle", b"resampler_handle", "sample_rate", b"sample_rate"]) -> None: ... global___RemixAndResampleRequest = RemixAndResampleRequest -@typing_extensions.final +@typing.final class RemixAndResampleResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -274,12 +503,328 @@ class RemixAndResampleResponse(google.protobuf.message.Message): *, buffer: global___OwnedAudioFrameBuffer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer"]) -> None: ... global___RemixAndResampleResponse = RemixAndResampleResponse -@typing_extensions.final +@typing.final +class NewApmRequest(google.protobuf.message.Message): + """AEC""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ECHO_CANCELLER_ENABLED_FIELD_NUMBER: builtins.int + GAIN_CONTROLLER_ENABLED_FIELD_NUMBER: builtins.int + HIGH_PASS_FILTER_ENABLED_FIELD_NUMBER: builtins.int + NOISE_SUPPRESSION_ENABLED_FIELD_NUMBER: builtins.int + echo_canceller_enabled: builtins.bool + gain_controller_enabled: builtins.bool + high_pass_filter_enabled: builtins.bool + noise_suppression_enabled: builtins.bool + def __init__( + self, + *, + echo_canceller_enabled: builtins.bool | None = ..., + gain_controller_enabled: builtins.bool | None = ..., + high_pass_filter_enabled: builtins.bool | None = ..., + noise_suppression_enabled: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["echo_canceller_enabled", b"echo_canceller_enabled", "gain_controller_enabled", b"gain_controller_enabled", "high_pass_filter_enabled", b"high_pass_filter_enabled", "noise_suppression_enabled", b"noise_suppression_enabled"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["echo_canceller_enabled", b"echo_canceller_enabled", "gain_controller_enabled", b"gain_controller_enabled", "high_pass_filter_enabled", b"high_pass_filter_enabled", "noise_suppression_enabled", b"noise_suppression_enabled"]) -> None: ... + +global___NewApmRequest = NewApmRequest + +@typing.final +class NewApmResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + APM_FIELD_NUMBER: builtins.int + @property + def apm(self) -> global___OwnedApm: ... + def __init__( + self, + *, + apm: global___OwnedApm | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["apm", b"apm"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm", b"apm"]) -> None: ... + +global___NewApmResponse = NewApmResponse + +@typing.final +class ApmProcessStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + APM_HANDLE_FIELD_NUMBER: builtins.int + DATA_PTR_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + SAMPLE_RATE_FIELD_NUMBER: builtins.int + NUM_CHANNELS_FIELD_NUMBER: builtins.int + apm_handle: builtins.int + data_ptr: builtins.int + """*mut i16""" + size: builtins.int + """in bytes""" + sample_rate: builtins.int + num_channels: builtins.int + def __init__( + self, + *, + apm_handle: builtins.int | None = ..., + data_ptr: builtins.int | None = ..., + size: builtins.int | None = ..., + sample_rate: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "size", b"size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "size", b"size"]) -> None: ... + +global___ApmProcessStreamRequest = ApmProcessStreamRequest + +@typing.final +class ApmProcessStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___ApmProcessStreamResponse = ApmProcessStreamResponse + +@typing.final +class ApmProcessReverseStreamRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + APM_HANDLE_FIELD_NUMBER: builtins.int + DATA_PTR_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + SAMPLE_RATE_FIELD_NUMBER: builtins.int + NUM_CHANNELS_FIELD_NUMBER: builtins.int + apm_handle: builtins.int + data_ptr: builtins.int + """*mut i16""" + size: builtins.int + """in bytes""" + sample_rate: builtins.int + num_channels: builtins.int + def __init__( + self, + *, + apm_handle: builtins.int | None = ..., + data_ptr: builtins.int | None = ..., + size: builtins.int | None = ..., + sample_rate: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "size", b"size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "size", b"size"]) -> None: ... + +global___ApmProcessReverseStreamRequest = ApmProcessReverseStreamRequest + +@typing.final +class ApmProcessReverseStreamResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___ApmProcessReverseStreamResponse = ApmProcessReverseStreamResponse + +@typing.final +class ApmSetStreamDelayRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + APM_HANDLE_FIELD_NUMBER: builtins.int + DELAY_MS_FIELD_NUMBER: builtins.int + apm_handle: builtins.int + delay_ms: builtins.int + def __init__( + self, + *, + apm_handle: builtins.int | None = ..., + delay_ms: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "delay_ms", b"delay_ms"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm_handle", b"apm_handle", "delay_ms", b"delay_ms"]) -> None: ... + +global___ApmSetStreamDelayRequest = ApmSetStreamDelayRequest + +@typing.final +class ApmSetStreamDelayResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___ApmSetStreamDelayResponse = ApmSetStreamDelayResponse + +@typing.final +class NewSoxResamplerRequest(google.protobuf.message.Message): + """New resampler using SoX (much better quality)""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INPUT_RATE_FIELD_NUMBER: builtins.int + OUTPUT_RATE_FIELD_NUMBER: builtins.int + NUM_CHANNELS_FIELD_NUMBER: builtins.int + INPUT_DATA_TYPE_FIELD_NUMBER: builtins.int + OUTPUT_DATA_TYPE_FIELD_NUMBER: builtins.int + QUALITY_RECIPE_FIELD_NUMBER: builtins.int + FLAGS_FIELD_NUMBER: builtins.int + input_rate: builtins.float + output_rate: builtins.float + num_channels: builtins.int + input_data_type: global___SoxResamplerDataType.ValueType + output_data_type: global___SoxResamplerDataType.ValueType + quality_recipe: global___SoxQualityRecipe.ValueType + flags: builtins.int + def __init__( + self, + *, + input_rate: builtins.float | None = ..., + output_rate: builtins.float | None = ..., + num_channels: builtins.int | None = ..., + input_data_type: global___SoxResamplerDataType.ValueType | None = ..., + output_data_type: global___SoxResamplerDataType.ValueType | None = ..., + quality_recipe: global___SoxQualityRecipe.ValueType | None = ..., + flags: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["flags", b"flags", "input_data_type", b"input_data_type", "input_rate", b"input_rate", "num_channels", b"num_channels", "output_data_type", b"output_data_type", "output_rate", b"output_rate", "quality_recipe", b"quality_recipe"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["flags", b"flags", "input_data_type", b"input_data_type", "input_rate", b"input_rate", "num_channels", b"num_channels", "output_data_type", b"output_data_type", "output_rate", b"output_rate", "quality_recipe", b"quality_recipe"]) -> None: ... + +global___NewSoxResamplerRequest = NewSoxResamplerRequest + +@typing.final +class NewSoxResamplerResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESAMPLER_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + @property + def resampler(self) -> global___OwnedSoxResampler: ... + def __init__( + self, + *, + resampler: global___OwnedSoxResampler | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "message", b"message", "resampler", b"resampler"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "message", b"message", "resampler", b"resampler"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["resampler", "error"] | None: ... + +global___NewSoxResamplerResponse = NewSoxResamplerResponse + +@typing.final +class PushSoxResamplerRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESAMPLER_HANDLE_FIELD_NUMBER: builtins.int + DATA_PTR_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + resampler_handle: builtins.int + data_ptr: builtins.int + """*const i16""" + size: builtins.int + """in bytes""" + def __init__( + self, + *, + resampler_handle: builtins.int | None = ..., + data_ptr: builtins.int | None = ..., + size: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "resampler_handle", b"resampler_handle", "size", b"size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "resampler_handle", b"resampler_handle", "size", b"size"]) -> None: ... + +global___PushSoxResamplerRequest = PushSoxResamplerRequest + +@typing.final +class PushSoxResamplerResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OUTPUT_PTR_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + output_ptr: builtins.int + """*const i16 (could be null)""" + size: builtins.int + """in bytes""" + error: builtins.str + def __init__( + self, + *, + output_ptr: builtins.int | None = ..., + size: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "output_ptr", b"output_ptr", "size", b"size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "output_ptr", b"output_ptr", "size", b"size"]) -> None: ... + +global___PushSoxResamplerResponse = PushSoxResamplerResponse + +@typing.final +class FlushSoxResamplerRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RESAMPLER_HANDLE_FIELD_NUMBER: builtins.int + resampler_handle: builtins.int + def __init__( + self, + *, + resampler_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["resampler_handle", b"resampler_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["resampler_handle", b"resampler_handle"]) -> None: ... + +global___FlushSoxResamplerRequest = FlushSoxResamplerRequest + +@typing.final +class FlushSoxResamplerResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OUTPUT_PTR_FIELD_NUMBER: builtins.int + SIZE_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + output_ptr: builtins.int + """*const i16 (could be null)""" + size: builtins.int + """in bytes""" + error: builtins.str + def __init__( + self, + *, + output_ptr: builtins.int | None = ..., + size: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "output_ptr", b"output_ptr", "size", b"size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "output_ptr", b"output_ptr", "size", b"size"]) -> None: ... + +global___FlushSoxResamplerResponse = FlushSoxResamplerResponse + +@typing.final class AudioFrameBufferInfo(google.protobuf.message.Message): """ AudioFrame buffer @@ -299,16 +844,17 @@ class AudioFrameBufferInfo(google.protobuf.message.Message): def __init__( self, *, - data_ptr: builtins.int = ..., - num_channels: builtins.int = ..., - sample_rate: builtins.int = ..., - samples_per_channel: builtins.int = ..., + data_ptr: builtins.int | None = ..., + num_channels: builtins.int | None = ..., + sample_rate: builtins.int | None = ..., + samples_per_channel: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "samples_per_channel", b"samples_per_channel"]) -> None: ... + def HasField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "samples_per_channel", b"samples_per_channel"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "num_channels", b"num_channels", "sample_rate", b"sample_rate", "samples_per_channel", b"samples_per_channel"]) -> None: ... global___AudioFrameBufferInfo = AudioFrameBufferInfo -@typing_extensions.final +@typing.final class OwnedAudioFrameBuffer(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -324,12 +870,12 @@ class OwnedAudioFrameBuffer(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___AudioFrameBufferInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedAudioFrameBuffer = OwnedAudioFrameBuffer -@typing_extensions.final +@typing.final class AudioStreamInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -338,13 +884,14 @@ class AudioStreamInfo(google.protobuf.message.Message): def __init__( self, *, - type: global___AudioStreamType.ValueType = ..., + type: global___AudioStreamType.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["type", b"type"]) -> None: ... global___AudioStreamInfo = AudioStreamInfo -@typing_extensions.final +@typing.final class OwnedAudioStream(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -360,12 +907,12 @@ class OwnedAudioStream(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___AudioStreamInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedAudioStream = OwnedAudioStream -@typing_extensions.final +@typing.final class AudioStreamEvent(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -380,17 +927,17 @@ class AudioStreamEvent(google.protobuf.message.Message): def __init__( self, *, - stream_handle: builtins.int = ..., + stream_handle: builtins.int | None = ..., frame_received: global___AudioFrameReceived | None = ..., eos: global___AudioStreamEOS | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["frame_received", "eos"] | None: ... + def HasField(self, field_name: typing.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["frame_received", "eos"] | None: ... global___AudioStreamEvent = AudioStreamEvent -@typing_extensions.final +@typing.final class AudioFrameReceived(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -402,12 +949,12 @@ class AudioFrameReceived(google.protobuf.message.Message): *, frame: global___OwnedAudioFrameBuffer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["frame", b"frame"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["frame", b"frame"]) -> None: ... + def HasField(self, field_name: typing.Literal["frame", b"frame"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame", b"frame"]) -> None: ... global___AudioFrameReceived = AudioFrameReceived -@typing_extensions.final +@typing.final class AudioStreamEOS(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -417,7 +964,7 @@ class AudioStreamEOS(google.protobuf.message.Message): global___AudioStreamEOS = AudioStreamEOS -@typing_extensions.final +@typing.final class AudioSourceOptions(google.protobuf.message.Message): """ AudioSource @@ -428,21 +975,28 @@ class AudioSourceOptions(google.protobuf.message.Message): ECHO_CANCELLATION_FIELD_NUMBER: builtins.int NOISE_SUPPRESSION_FIELD_NUMBER: builtins.int AUTO_GAIN_CONTROL_FIELD_NUMBER: builtins.int + PREFER_HARDWARE_FIELD_NUMBER: builtins.int echo_cancellation: builtins.bool noise_suppression: builtins.bool auto_gain_control: builtins.bool + prefer_hardware: builtins.bool + """Prefer hardware audio processing (e.g., iOS VPIO). Lower latency. + Only applies to AudioSourcePlatform. Default: true. + """ def __init__( self, *, - echo_cancellation: builtins.bool = ..., - noise_suppression: builtins.bool = ..., - auto_gain_control: builtins.bool = ..., + echo_cancellation: builtins.bool | None = ..., + noise_suppression: builtins.bool | None = ..., + auto_gain_control: builtins.bool | None = ..., + prefer_hardware: builtins.bool | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["auto_gain_control", b"auto_gain_control", "echo_cancellation", b"echo_cancellation", "noise_suppression", b"noise_suppression"]) -> None: ... + def HasField(self, field_name: typing.Literal["auto_gain_control", b"auto_gain_control", "echo_cancellation", b"echo_cancellation", "noise_suppression", b"noise_suppression", "prefer_hardware", b"prefer_hardware"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["auto_gain_control", b"auto_gain_control", "echo_cancellation", b"echo_cancellation", "noise_suppression", b"noise_suppression", "prefer_hardware", b"prefer_hardware"]) -> None: ... global___AudioSourceOptions = AudioSourceOptions -@typing_extensions.final +@typing.final class AudioSourceInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -451,13 +1005,14 @@ class AudioSourceInfo(google.protobuf.message.Message): def __init__( self, *, - type: global___AudioSourceType.ValueType = ..., + type: global___AudioSourceType.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["type", b"type"]) -> None: ... global___AudioSourceInfo = AudioSourceInfo -@typing_extensions.final +@typing.final class OwnedAudioSource(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -473,12 +1028,12 @@ class OwnedAudioSource(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___AudioSourceInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedAudioSource = OwnedAudioSource -@typing_extensions.final +@typing.final class AudioResamplerInfo(google.protobuf.message.Message): """ AudioResampler @@ -492,7 +1047,7 @@ class AudioResamplerInfo(google.protobuf.message.Message): global___AudioResamplerInfo = AudioResamplerInfo -@typing_extensions.final +@typing.final class OwnedAudioResampler(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -508,7 +1063,498 @@ class OwnedAudioResampler(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___AudioResamplerInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedAudioResampler = OwnedAudioResampler + +@typing.final +class OwnedApm(google.protobuf.message.Message): + """ + AEC + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle"]) -> None: ... + +global___OwnedApm = OwnedApm + +@typing.final +class SoxResamplerInfo(google.protobuf.message.Message): + """ + Sox AudioResampler + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___SoxResamplerInfo = SoxResamplerInfo + +@typing.final +class OwnedSoxResampler(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___SoxResamplerInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___SoxResamplerInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedSoxResampler = OwnedSoxResampler + +@typing.final +class LoadAudioFilterPluginRequest(google.protobuf.message.Message): + """Audio Filter Plugin""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLUGIN_PATH_FIELD_NUMBER: builtins.int + DEPENDENCIES_FIELD_NUMBER: builtins.int + MODULE_ID_FIELD_NUMBER: builtins.int + plugin_path: builtins.str + """path for ffi audio filter plugin""" + module_id: builtins.str + """Unique identifier of the plugin""" + @property + def dependencies(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """Optional: paths for dependency dylibs""" + + def __init__( + self, + *, + plugin_path: builtins.str | None = ..., + dependencies: collections.abc.Iterable[builtins.str] | None = ..., + module_id: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["module_id", b"module_id", "plugin_path", b"plugin_path"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["dependencies", b"dependencies", "module_id", b"module_id", "plugin_path", b"plugin_path"]) -> None: ... + +global___LoadAudioFilterPluginRequest = LoadAudioFilterPluginRequest + +@typing.final +class LoadAudioFilterPluginResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___LoadAudioFilterPluginResponse = LoadAudioFilterPluginResponse + +@typing.final +class AudioDeviceInfo(google.protobuf.message.Message): + """ + PlatformAudio - Platform audio device management via WebRTC's ADM + + PlatformAudio provides access to the platform's audio devices (microphones and + speakers) via WebRTC's Audio Device Module (ADM). Use it to: + + - Capture audio from the microphone for publishing + - Play received audio through the speakers + - Enumerate and select audio devices + + # Usage + + 1. Create a PlatformAudio handle with NewPlatformAudioRequest + 2. Enumerate devices with GetAudioDevicesRequest (optional) + 3. Select devices with SetRecordingDeviceRequest/SetPlayoutDeviceRequest (optional) + 4. Create an audio track using AudioSourcePlatform type + 5. When done, drop the handle (the ADM is disabled when all handles are released) + + Information about an audio device. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + INDEX_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + GUID_FIELD_NUMBER: builtins.int + index: builtins.int + """Device index (0-based). Note: indices can change when devices are added/removed.""" + name: builtins.str + """Device name as reported by the operating system.""" + guid: builtins.str + """Platform-specific unique device identifier (GUID). + This is stable across device additions/removals and should be preferred + over index for device selection. Format varies by platform: + - Windows: Device interface path + - macOS: AudioObjectID as string + - Linux: ALSA/PulseAudio device identifier + """ + def __init__( + self, + *, + index: builtins.int | None = ..., + name: builtins.str | None = ..., + guid: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["guid", b"guid", "index", b"index", "name", b"name"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["guid", b"guid", "index", b"index", "name", b"name"]) -> None: ... + +global___AudioDeviceInfo = AudioDeviceInfo + +@typing.final +class PlatformAudioInfo(google.protobuf.message.Message): + """Information about a PlatformAudio instance.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + RECORDING_DEVICE_COUNT_FIELD_NUMBER: builtins.int + PLAYOUT_DEVICE_COUNT_FIELD_NUMBER: builtins.int + recording_device_count: builtins.int + """Number of available recording (microphone) devices.""" + playout_device_count: builtins.int + """Number of available playout (speaker) devices.""" + def __init__( + self, + *, + recording_device_count: builtins.int | None = ..., + playout_device_count: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["playout_device_count", b"playout_device_count", "recording_device_count", b"recording_device_count"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["playout_device_count", b"playout_device_count", "recording_device_count", b"recording_device_count"]) -> None: ... + +global___PlatformAudioInfo = PlatformAudioInfo + +@typing.final +class OwnedPlatformAudio(google.protobuf.message.Message): + """Owned PlatformAudio handle with info.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___PlatformAudioInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___PlatformAudioInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedPlatformAudio = OwnedPlatformAudio + +@typing.final +class NewPlatformAudioRequest(google.protobuf.message.Message): + """Create a new PlatformAudio instance. + + This enables the platform ADM for microphone capture and speaker playout. + If another PlatformAudio instance exists, this reuses the same underlying ADM. + + The returned handle must be kept alive while platform audio is needed. + When all handles are released, the ADM is automatically disabled. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___NewPlatformAudioRequest = NewPlatformAudioRequest + +@typing.final +class NewPlatformAudioResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if creation failed.""" + @property + def platform_audio(self) -> global___OwnedPlatformAudio: + """The PlatformAudio handle on success.""" + + def __init__( + self, + *, + platform_audio: global___OwnedPlatformAudio | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "message", b"message", "platform_audio", b"platform_audio"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "message", b"message", "platform_audio", b"platform_audio"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["platform_audio", "error"] | None: ... + +global___NewPlatformAudioResponse = NewPlatformAudioResponse + +@typing.final +class GetAudioDevicesRequest(google.protobuf.message.Message): + """Get available audio devices. + + Returns lists of available recording (microphone) and playout (speaker) devices. + + # Platform Notes + + - Desktop (Windows/macOS/Linux): Returns all available devices with names and GUIDs. + - Mobile (iOS/Android): Returns only one "default" device with empty name and GUID. + Device enumeration is not meaningful on mobile - use for device count only. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int + platform_audio_handle: builtins.int + """The PlatformAudio handle.""" + def __init__( + self, + *, + platform_audio_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> None: ... + +global___GetAudioDevicesRequest = GetAudioDevicesRequest + +@typing.final +class GetAudioDevicesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLAYOUT_DEVICES_FIELD_NUMBER: builtins.int + RECORDING_DEVICES_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if enumeration failed, empty/absent on success.""" + @property + def playout_devices(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AudioDeviceInfo]: + """Available playout devices (speakers/headphones).""" + + @property + def recording_devices(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AudioDeviceInfo]: + """Available recording devices (microphones).""" + + def __init__( + self, + *, + playout_devices: collections.abc.Iterable[global___AudioDeviceInfo] | None = ..., + recording_devices: collections.abc.Iterable[global___AudioDeviceInfo] | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "playout_devices", b"playout_devices", "recording_devices", b"recording_devices"]) -> None: ... + +global___GetAudioDevicesResponse = GetAudioDevicesResponse + +@typing.final +class SetRecordingDeviceRequest(google.protobuf.message.Message): + """Set the recording device (microphone). + + Call this before creating audio tracks to select which microphone to use. + Use the GUID from AudioDeviceInfo for stable device selection across hot-plug events. + + # Platform Notes + + - Desktop: Works as expected - selects from enumerated devices. + - Mobile (iOS/Android): No-op. Mobile platforms handle microphone selection at the + system level. This will succeed but has no effect. Skip calling on mobile. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int + DEVICE_ID_FIELD_NUMBER: builtins.int + platform_audio_handle: builtins.int + """The PlatformAudio handle.""" + device_id: builtins.str + """Device GUID from AudioDeviceInfo.guid - stable across device additions/removals.""" + def __init__( + self, + *, + platform_audio_handle: builtins.int | None = ..., + device_id: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["device_id", b"device_id", "platform_audio_handle", b"platform_audio_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["device_id", b"device_id", "platform_audio_handle", b"platform_audio_handle"]) -> None: ... + +global___SetRecordingDeviceRequest = SetRecordingDeviceRequest + +@typing.final +class SetRecordingDeviceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if the operation failed. + Empty/absent on success (including no-op success on mobile). + """ + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___SetRecordingDeviceResponse = SetRecordingDeviceResponse + +@typing.final +class SetPlayoutDeviceRequest(google.protobuf.message.Message): + """Set the playout device (speaker/headphones). + + Call this before connecting to select which speaker to use for audio output. + Use the GUID from AudioDeviceInfo for stable device selection across hot-plug events. + + # Platform Notes + + - Desktop: Works as expected - selects from enumerated devices. + - Mobile (iOS/Android): No-op. Mobile platforms handle audio routing at the system level. + This will succeed but has no effect. For audio routing on mobile: + - iOS: Use AVAudioSession to control speaker/earpiece/Bluetooth routing + - Android: Use AudioManager.setSpeakerphoneOn() to switch outputs + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int + DEVICE_ID_FIELD_NUMBER: builtins.int + platform_audio_handle: builtins.int + """The PlatformAudio handle.""" + device_id: builtins.str + """Device GUID from AudioDeviceInfo.guid - stable across device additions/removals.""" + def __init__( + self, + *, + platform_audio_handle: builtins.int | None = ..., + device_id: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["device_id", b"device_id", "platform_audio_handle", b"platform_audio_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["device_id", b"device_id", "platform_audio_handle", b"platform_audio_handle"]) -> None: ... + +global___SetPlayoutDeviceRequest = SetPlayoutDeviceRequest + +@typing.final +class SetPlayoutDeviceResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if the operation failed. + Empty/absent on success (including no-op success on mobile). + """ + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___SetPlayoutDeviceResponse = SetPlayoutDeviceResponse + +@typing.final +class StartRecordingRequest(google.protobuf.message.Message): + """Start recording from the microphone. + + Recording is started automatically when PlatformAudio is created. + Use this to resume recording after calling StopRecording. + This also turns on the system's recording privacy indicator. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int + platform_audio_handle: builtins.int + """The PlatformAudio handle.""" + def __init__( + self, + *, + platform_audio_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> None: ... + +global___StartRecordingRequest = StartRecordingRequest + +@typing.final +class StartRecordingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if the operation failed. + Empty/absent on success. + """ + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___StartRecordingResponse = StartRecordingResponse + +@typing.final +class StopRecordingRequest(google.protobuf.message.Message): + """Stop recording from the microphone. + + Use this to temporarily stop recording without disposing PlatformAudio. + This will turn off the system's recording privacy indicator (e.g., on macOS/iOS). + Call StartRecording to resume recording. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PLATFORM_AUDIO_HANDLE_FIELD_NUMBER: builtins.int + platform_audio_handle: builtins.int + """The PlatformAudio handle.""" + def __init__( + self, + *, + platform_audio_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["platform_audio_handle", b"platform_audio_handle"]) -> None: ... + +global___StopRecordingRequest = StopRecordingRequest + +@typing.final +class StopRecordingResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + """Error message if the operation failed. + Empty/absent on success. + """ + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___StopRecordingResponse = StopRecordingResponse diff --git a/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.py b/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.py new file mode 100644 index 00000000..12f113cd --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.py @@ -0,0 +1,171 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: data_stream.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import handle_pb2 as handle__pb2 +from . import e2ee_pb2 as e2ee__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11\x64\x61ta_stream.proto\x12\rlivekit.proto\x1a\x0chandle.proto\x1a\ne2ee.proto\"s\n\x15OwnedTextStreamReader\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12+\n\x04info\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.TextStreamInfo\"?\n&TextStreamReaderReadIncrementalRequest\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\")\n\'TextStreamReaderReadIncrementalResponse\"Q\n\x1eTextStreamReaderReadAllRequest\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\"3\n\x1fTextStreamReaderReadAllResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"}\n\x1fTextStreamReaderReadAllCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x11\n\x07\x63ontent\x18\x02 \x01(\tH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"\xb3\x01\n\x15TextStreamReaderEvent\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\x12\x46\n\x0e\x63hunk_received\x18\x02 \x01(\x0b\x32,.livekit.proto.TextStreamReaderChunkReceivedH\x00\x12\x31\n\x03\x65os\x18\x03 \x01(\x0b\x32\".livekit.proto.TextStreamReaderEOSH\x00\x42\x08\n\x06\x64\x65tail\"0\n\x1dTextStreamReaderChunkReceived\x12\x0f\n\x07\x63ontent\x18\x01 \x02(\t\"\xbb\x01\n\x13TextStreamReaderEOS\x12)\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x1a.livekit.proto.StreamError\x12\x46\n\nattributes\x18\x02 \x03(\x0b\x32\x32.livekit.proto.TextStreamReaderEOS.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"s\n\x15OwnedByteStreamReader\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12+\n\x04info\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.ByteStreamInfo\"?\n&ByteStreamReaderReadIncrementalRequest\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\")\n\'ByteStreamReaderReadIncrementalResponse\"Q\n\x1e\x42yteStreamReaderReadAllRequest\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\"3\n\x1f\x42yteStreamReaderReadAllResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"}\n\x1f\x42yteStreamReaderReadAllCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x11\n\x07\x63ontent\x18\x02 \x01(\x0cH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"\x7f\n\"ByteStreamReaderWriteToFileRequest\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\x12\x11\n\tdirectory\x18\x03 \x01(\t\x12\x15\n\rname_override\x18\x04 \x01(\t\"7\n#ByteStreamReaderWriteToFileResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x83\x01\n#ByteStreamReaderWriteToFileCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x13\n\tfile_path\x18\x02 \x01(\tH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"\xb3\x01\n\x15\x42yteStreamReaderEvent\x12\x15\n\rreader_handle\x18\x01 \x02(\x04\x12\x46\n\x0e\x63hunk_received\x18\x02 \x01(\x0b\x32,.livekit.proto.ByteStreamReaderChunkReceivedH\x00\x12\x31\n\x03\x65os\x18\x03 \x01(\x0b\x32\".livekit.proto.ByteStreamReaderEOSH\x00\x42\x08\n\x06\x64\x65tail\"0\n\x1d\x42yteStreamReaderChunkReceived\x12\x0f\n\x07\x63ontent\x18\x01 \x02(\x0c\"\xbb\x01\n\x13\x42yteStreamReaderEOS\x12)\n\x05\x65rror\x18\x01 \x01(\x0b\x32\x1a.livekit.proto.StreamError\x12\x46\n\nattributes\x18\x02 \x03(\x0b\x32\x32.livekit.proto.ByteStreamReaderEOS.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x99\x01\n\x15StreamSendFileRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x31\n\x07options\x18\x02 \x02(\x0b\x32 .livekit.proto.StreamByteOptions\x12\x11\n\tfile_path\x18\x03 \x02(\t\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"*\n\x16StreamSendFileResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x90\x01\n\x16StreamSendFileCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12-\n\x04info\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.ByteStreamInfoH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"\x96\x01\n\x16StreamSendBytesRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x31\n\x07options\x18\x02 \x02(\x0b\x32 .livekit.proto.StreamByteOptions\x12\r\n\x05\x62ytes\x18\x03 \x02(\x0c\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"+\n\x17StreamSendBytesResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x91\x01\n\x17StreamSendBytesCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12-\n\x04info\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.ByteStreamInfoH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"\x94\x01\n\x15StreamSendTextRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x31\n\x07options\x18\x02 \x02(\x0b\x32 .livekit.proto.StreamTextOptions\x12\x0c\n\x04text\x18\x03 \x02(\t\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"*\n\x16StreamSendTextResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x90\x01\n\x16StreamSendTextCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12-\n\x04info\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.TextStreamInfoH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"s\n\x15OwnedByteStreamWriter\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12+\n\x04info\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.ByteStreamInfo\"\x86\x01\n\x15\x42yteStreamOpenRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x31\n\x07options\x18\x02 \x02(\x0b\x32 .livekit.proto.StreamByteOptions\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"*\n\x16\x42yteStreamOpenResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x99\x01\n\x16\x42yteStreamOpenCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x36\n\x06writer\x18\x02 \x01(\x0b\x32$.livekit.proto.OwnedByteStreamWriterH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"^\n\x1c\x42yteStreamWriterWriteRequest\x12\x15\n\rwriter_handle\x18\x01 \x02(\x04\x12\r\n\x05\x62ytes\x18\x02 \x02(\x0c\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"1\n\x1d\x42yteStreamWriterWriteResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\\\n\x1d\x42yteStreamWriterWriteCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12)\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1a.livekit.proto.StreamError\"\xe3\x01\n\x1c\x42yteStreamWriterCloseRequest\x12\x15\n\rwriter_handle\x18\x01 \x02(\x04\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\x12O\n\nattributes\x18\x04 \x03(\x0b\x32;.livekit.proto.ByteStreamWriterCloseRequest.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"1\n\x1d\x42yteStreamWriterCloseResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\\\n\x1d\x42yteStreamWriterCloseCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12)\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1a.livekit.proto.StreamError\"s\n\x15OwnedTextStreamWriter\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12+\n\x04info\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.TextStreamInfo\"\x86\x01\n\x15TextStreamOpenRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x31\n\x07options\x18\x02 \x02(\x0b\x32 .livekit.proto.StreamTextOptions\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"*\n\x16TextStreamOpenResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x99\x01\n\x16TextStreamOpenCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x36\n\x06writer\x18\x02 \x01(\x0b\x32$.livekit.proto.OwnedTextStreamWriterH\x00\x12+\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.StreamErrorH\x00\x42\x08\n\x06result\"]\n\x1cTextStreamWriterWriteRequest\x12\x15\n\rwriter_handle\x18\x01 \x02(\x04\x12\x0c\n\x04text\x18\x02 \x02(\t\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"1\n\x1dTextStreamWriterWriteResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\\\n\x1dTextStreamWriterWriteCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12)\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1a.livekit.proto.StreamError\"\xe3\x01\n\x1cTextStreamWriterCloseRequest\x12\x15\n\rwriter_handle\x18\x01 \x02(\x04\x12\x0e\n\x06reason\x18\x02 \x01(\t\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\x12O\n\nattributes\x18\x04 \x03(\x0b\x32;.livekit.proto.TextStreamWriterCloseRequest.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"1\n\x1dTextStreamWriterCloseResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\\\n\x1dTextStreamWriterCloseCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12)\n\x05\x65rror\x18\x02 \x01(\x0b\x32\x1a.livekit.proto.StreamError\"\x81\x04\n\x0eTextStreamInfo\x12\x11\n\tstream_id\x18\x01 \x02(\t\x12\x11\n\ttimestamp\x18\x02 \x02(\x03\x12\x11\n\tmime_type\x18\x03 \x02(\t\x12\r\n\x05topic\x18\x04 \x02(\t\x12\x14\n\x0ctotal_length\x18\x05 \x01(\x04\x12\x41\n\nattributes\x18\x06 \x03(\x0b\x32-.livekit.proto.TextStreamInfo.AttributesEntry\x12\x43\n\x0eoperation_type\x18\x07 \x02(\x0e\x32+.livekit.proto.TextStreamInfo.OperationType\x12\x0f\n\x07version\x18\x08 \x01(\x05\x12\x1a\n\x12reply_to_stream_id\x18\t \x01(\t\x12\x1b\n\x13\x61ttached_stream_ids\x18\n \x03(\t\x12\x11\n\tgenerated\x18\x0b \x01(\x08\x12\x36\n\x0f\x65ncryption_type\x18\x0c \x02(\x0e\x32\x1d.livekit.proto.EncryptionType\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"A\n\rOperationType\x12\n\n\x06\x43REATE\x10\x00\x12\n\n\x06UPDATE\x10\x01\x12\n\n\x06\x44\x45LETE\x10\x02\x12\x0c\n\x08REACTION\x10\x03\"\xaa\x02\n\x0e\x42yteStreamInfo\x12\x11\n\tstream_id\x18\x01 \x02(\t\x12\x11\n\ttimestamp\x18\x02 \x02(\x03\x12\x11\n\tmime_type\x18\x03 \x02(\t\x12\r\n\x05topic\x18\x04 \x02(\t\x12\x14\n\x0ctotal_length\x18\x05 \x01(\x04\x12\x41\n\nattributes\x18\x06 \x03(\x0b\x32-.livekit.proto.ByteStreamInfo.AttributesEntry\x12\x0c\n\x04name\x18\x07 \x02(\t\x12\x36\n\x0f\x65ncryption_type\x18\x08 \x02(\x0e\x32\x1d.livekit.proto.EncryptionType\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\x94\x03\n\x11StreamTextOptions\x12\r\n\x05topic\x18\x01 \x02(\t\x12\x44\n\nattributes\x18\x02 \x03(\x0b\x32\x30.livekit.proto.StreamTextOptions.AttributesEntry\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\n\n\x02id\x18\x04 \x01(\t\x12\x43\n\x0eoperation_type\x18\x05 \x01(\x0e\x32+.livekit.proto.TextStreamInfo.OperationType\x12\x0f\n\x07version\x18\x06 \x01(\x05\x12\x1a\n\x12reply_to_stream_id\x18\x07 \x01(\t\x12\x1b\n\x13\x61ttached_stream_ids\x18\x08 \x03(\t\x12\x11\n\tgenerated\x18\t \x01(\x08\x12\x10\n\x08\x63ompress\x18\n \x01(\x08\x12\x17\n\x0fsender_identity\x18\x0b \x01(\t\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xa9\x02\n\x11StreamByteOptions\x12\r\n\x05topic\x18\x01 \x02(\t\x12\x44\n\nattributes\x18\x02 \x03(\x0b\x32\x30.livekit.proto.StreamByteOptions.AttributesEntry\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\n\n\x02id\x18\x04 \x01(\t\x12\x0c\n\x04name\x18\x05 \x01(\t\x12\x11\n\tmime_type\x18\x06 \x01(\t\x12\x14\n\x0ctotal_length\x18\x07 \x01(\x04\x12\x10\n\x08\x63ompress\x18\x08 \x01(\x08\x12\x17\n\x0fsender_identity\x18\t \x01(\t\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\"\n\x0bStreamError\x12\x13\n\x0b\x64\x65scription\x18\x01 \x02(\tB\x10\xaa\x02\rLiveKit.Proto') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_stream_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' + _globals['_TEXTSTREAMREADEREOS_ATTRIBUTESENTRY']._options = None + _globals['_TEXTSTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_BYTESTREAMREADEREOS_ATTRIBUTESENTRY']._options = None + _globals['_BYTESTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_BYTESTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._options = None + _globals['_BYTESTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_TEXTSTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._options = None + _globals['_TEXTSTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_TEXTSTREAMINFO_ATTRIBUTESENTRY']._options = None + _globals['_TEXTSTREAMINFO_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_BYTESTREAMINFO_ATTRIBUTESENTRY']._options = None + _globals['_BYTESTREAMINFO_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_STREAMTEXTOPTIONS_ATTRIBUTESENTRY']._options = None + _globals['_STREAMTEXTOPTIONS_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_STREAMBYTEOPTIONS_ATTRIBUTESENTRY']._options = None + _globals['_STREAMBYTEOPTIONS_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_OWNEDTEXTSTREAMREADER']._serialized_start=62 + _globals['_OWNEDTEXTSTREAMREADER']._serialized_end=177 + _globals['_TEXTSTREAMREADERREADINCREMENTALREQUEST']._serialized_start=179 + _globals['_TEXTSTREAMREADERREADINCREMENTALREQUEST']._serialized_end=242 + _globals['_TEXTSTREAMREADERREADINCREMENTALRESPONSE']._serialized_start=244 + _globals['_TEXTSTREAMREADERREADINCREMENTALRESPONSE']._serialized_end=285 + _globals['_TEXTSTREAMREADERREADALLREQUEST']._serialized_start=287 + _globals['_TEXTSTREAMREADERREADALLREQUEST']._serialized_end=368 + _globals['_TEXTSTREAMREADERREADALLRESPONSE']._serialized_start=370 + _globals['_TEXTSTREAMREADERREADALLRESPONSE']._serialized_end=421 + _globals['_TEXTSTREAMREADERREADALLCALLBACK']._serialized_start=423 + _globals['_TEXTSTREAMREADERREADALLCALLBACK']._serialized_end=548 + _globals['_TEXTSTREAMREADEREVENT']._serialized_start=551 + _globals['_TEXTSTREAMREADEREVENT']._serialized_end=730 + _globals['_TEXTSTREAMREADERCHUNKRECEIVED']._serialized_start=732 + _globals['_TEXTSTREAMREADERCHUNKRECEIVED']._serialized_end=780 + _globals['_TEXTSTREAMREADEREOS']._serialized_start=783 + _globals['_TEXTSTREAMREADEREOS']._serialized_end=970 + _globals['_TEXTSTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_TEXTSTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_OWNEDBYTESTREAMREADER']._serialized_start=972 + _globals['_OWNEDBYTESTREAMREADER']._serialized_end=1087 + _globals['_BYTESTREAMREADERREADINCREMENTALREQUEST']._serialized_start=1089 + _globals['_BYTESTREAMREADERREADINCREMENTALREQUEST']._serialized_end=1152 + _globals['_BYTESTREAMREADERREADINCREMENTALRESPONSE']._serialized_start=1154 + _globals['_BYTESTREAMREADERREADINCREMENTALRESPONSE']._serialized_end=1195 + _globals['_BYTESTREAMREADERREADALLREQUEST']._serialized_start=1197 + _globals['_BYTESTREAMREADERREADALLREQUEST']._serialized_end=1278 + _globals['_BYTESTREAMREADERREADALLRESPONSE']._serialized_start=1280 + _globals['_BYTESTREAMREADERREADALLRESPONSE']._serialized_end=1331 + _globals['_BYTESTREAMREADERREADALLCALLBACK']._serialized_start=1333 + _globals['_BYTESTREAMREADERREADALLCALLBACK']._serialized_end=1458 + _globals['_BYTESTREAMREADERWRITETOFILEREQUEST']._serialized_start=1460 + _globals['_BYTESTREAMREADERWRITETOFILEREQUEST']._serialized_end=1587 + _globals['_BYTESTREAMREADERWRITETOFILERESPONSE']._serialized_start=1589 + _globals['_BYTESTREAMREADERWRITETOFILERESPONSE']._serialized_end=1644 + _globals['_BYTESTREAMREADERWRITETOFILECALLBACK']._serialized_start=1647 + _globals['_BYTESTREAMREADERWRITETOFILECALLBACK']._serialized_end=1778 + _globals['_BYTESTREAMREADEREVENT']._serialized_start=1781 + _globals['_BYTESTREAMREADEREVENT']._serialized_end=1960 + _globals['_BYTESTREAMREADERCHUNKRECEIVED']._serialized_start=1962 + _globals['_BYTESTREAMREADERCHUNKRECEIVED']._serialized_end=2010 + _globals['_BYTESTREAMREADEREOS']._serialized_start=2013 + _globals['_BYTESTREAMREADEREOS']._serialized_end=2200 + _globals['_BYTESTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_BYTESTREAMREADEREOS_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_STREAMSENDFILEREQUEST']._serialized_start=2203 + _globals['_STREAMSENDFILEREQUEST']._serialized_end=2356 + _globals['_STREAMSENDFILERESPONSE']._serialized_start=2358 + _globals['_STREAMSENDFILERESPONSE']._serialized_end=2400 + _globals['_STREAMSENDFILECALLBACK']._serialized_start=2403 + _globals['_STREAMSENDFILECALLBACK']._serialized_end=2547 + _globals['_STREAMSENDBYTESREQUEST']._serialized_start=2550 + _globals['_STREAMSENDBYTESREQUEST']._serialized_end=2700 + _globals['_STREAMSENDBYTESRESPONSE']._serialized_start=2702 + _globals['_STREAMSENDBYTESRESPONSE']._serialized_end=2745 + _globals['_STREAMSENDBYTESCALLBACK']._serialized_start=2748 + _globals['_STREAMSENDBYTESCALLBACK']._serialized_end=2893 + _globals['_STREAMSENDTEXTREQUEST']._serialized_start=2896 + _globals['_STREAMSENDTEXTREQUEST']._serialized_end=3044 + _globals['_STREAMSENDTEXTRESPONSE']._serialized_start=3046 + _globals['_STREAMSENDTEXTRESPONSE']._serialized_end=3088 + _globals['_STREAMSENDTEXTCALLBACK']._serialized_start=3091 + _globals['_STREAMSENDTEXTCALLBACK']._serialized_end=3235 + _globals['_OWNEDBYTESTREAMWRITER']._serialized_start=3237 + _globals['_OWNEDBYTESTREAMWRITER']._serialized_end=3352 + _globals['_BYTESTREAMOPENREQUEST']._serialized_start=3355 + _globals['_BYTESTREAMOPENREQUEST']._serialized_end=3489 + _globals['_BYTESTREAMOPENRESPONSE']._serialized_start=3491 + _globals['_BYTESTREAMOPENRESPONSE']._serialized_end=3533 + _globals['_BYTESTREAMOPENCALLBACK']._serialized_start=3536 + _globals['_BYTESTREAMOPENCALLBACK']._serialized_end=3689 + _globals['_BYTESTREAMWRITERWRITEREQUEST']._serialized_start=3691 + _globals['_BYTESTREAMWRITERWRITEREQUEST']._serialized_end=3785 + _globals['_BYTESTREAMWRITERWRITERESPONSE']._serialized_start=3787 + _globals['_BYTESTREAMWRITERWRITERESPONSE']._serialized_end=3836 + _globals['_BYTESTREAMWRITERWRITECALLBACK']._serialized_start=3838 + _globals['_BYTESTREAMWRITERWRITECALLBACK']._serialized_end=3930 + _globals['_BYTESTREAMWRITERCLOSEREQUEST']._serialized_start=3933 + _globals['_BYTESTREAMWRITERCLOSEREQUEST']._serialized_end=4160 + _globals['_BYTESTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_BYTESTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_BYTESTREAMWRITERCLOSERESPONSE']._serialized_start=4162 + _globals['_BYTESTREAMWRITERCLOSERESPONSE']._serialized_end=4211 + _globals['_BYTESTREAMWRITERCLOSECALLBACK']._serialized_start=4213 + _globals['_BYTESTREAMWRITERCLOSECALLBACK']._serialized_end=4305 + _globals['_OWNEDTEXTSTREAMWRITER']._serialized_start=4307 + _globals['_OWNEDTEXTSTREAMWRITER']._serialized_end=4422 + _globals['_TEXTSTREAMOPENREQUEST']._serialized_start=4425 + _globals['_TEXTSTREAMOPENREQUEST']._serialized_end=4559 + _globals['_TEXTSTREAMOPENRESPONSE']._serialized_start=4561 + _globals['_TEXTSTREAMOPENRESPONSE']._serialized_end=4603 + _globals['_TEXTSTREAMOPENCALLBACK']._serialized_start=4606 + _globals['_TEXTSTREAMOPENCALLBACK']._serialized_end=4759 + _globals['_TEXTSTREAMWRITERWRITEREQUEST']._serialized_start=4761 + _globals['_TEXTSTREAMWRITERWRITEREQUEST']._serialized_end=4854 + _globals['_TEXTSTREAMWRITERWRITERESPONSE']._serialized_start=4856 + _globals['_TEXTSTREAMWRITERWRITERESPONSE']._serialized_end=4905 + _globals['_TEXTSTREAMWRITERWRITECALLBACK']._serialized_start=4907 + _globals['_TEXTSTREAMWRITERWRITECALLBACK']._serialized_end=4999 + _globals['_TEXTSTREAMWRITERCLOSEREQUEST']._serialized_start=5002 + _globals['_TEXTSTREAMWRITERCLOSEREQUEST']._serialized_end=5229 + _globals['_TEXTSTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_TEXTSTREAMWRITERCLOSEREQUEST_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_TEXTSTREAMWRITERCLOSERESPONSE']._serialized_start=5231 + _globals['_TEXTSTREAMWRITERCLOSERESPONSE']._serialized_end=5280 + _globals['_TEXTSTREAMWRITERCLOSECALLBACK']._serialized_start=5282 + _globals['_TEXTSTREAMWRITERCLOSECALLBACK']._serialized_end=5374 + _globals['_TEXTSTREAMINFO']._serialized_start=5377 + _globals['_TEXTSTREAMINFO']._serialized_end=5890 + _globals['_TEXTSTREAMINFO_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_TEXTSTREAMINFO_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_TEXTSTREAMINFO_OPERATIONTYPE']._serialized_start=5825 + _globals['_TEXTSTREAMINFO_OPERATIONTYPE']._serialized_end=5890 + _globals['_BYTESTREAMINFO']._serialized_start=5893 + _globals['_BYTESTREAMINFO']._serialized_end=6191 + _globals['_BYTESTREAMINFO_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_BYTESTREAMINFO_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_STREAMTEXTOPTIONS']._serialized_start=6194 + _globals['_STREAMTEXTOPTIONS']._serialized_end=6598 + _globals['_STREAMTEXTOPTIONS_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_STREAMTEXTOPTIONS_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_STREAMBYTEOPTIONS']._serialized_start=6601 + _globals['_STREAMBYTEOPTIONS']._serialized_end=6898 + _globals['_STREAMBYTEOPTIONS_ATTRIBUTESENTRY']._serialized_start=921 + _globals['_STREAMBYTEOPTIONS_ATTRIBUTESENTRY']._serialized_end=970 + _globals['_STREAMERROR']._serialized_start=6900 + _globals['_STREAMERROR']._serialized_end=6934 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.pyi new file mode 100644 index 00000000..82ce4d89 --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/data_stream_pb2.pyi @@ -0,0 +1,1498 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2025 LiveKit, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import builtins +import collections.abc +from . import e2ee_pb2 +import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import handle_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class OwnedTextStreamReader(google.protobuf.message.Message): + """MARK: - Text stream reader + + A reader for an incoming stream. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___TextStreamInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___TextStreamInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedTextStreamReader = OwnedTextStreamReader + +@typing.final +class TextStreamReaderReadIncrementalRequest(google.protobuf.message.Message): + """Reads an incoming text stream incrementally.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reader_handle", b"reader_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reader_handle", b"reader_handle"]) -> None: ... + +global___TextStreamReaderReadIncrementalRequest = TextStreamReaderReadIncrementalRequest + +@typing.final +class TextStreamReaderReadIncrementalResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___TextStreamReaderReadIncrementalResponse = TextStreamReaderReadIncrementalResponse + +@typing.final +class TextStreamReaderReadAllRequest(google.protobuf.message.Message): + """Reads an incoming text stream in its entirety.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + request_async_id: builtins.int + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> None: ... + +global___TextStreamReaderReadAllRequest = TextStreamReaderReadAllRequest + +@typing.final +class TextStreamReaderReadAllResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___TextStreamReaderReadAllResponse = TextStreamReaderReadAllResponse + +@typing.final +class TextStreamReaderReadAllCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + CONTENT_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + content: builtins.str + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + content: builtins.str | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "content", b"content", "error", b"error", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "content", b"content", "error", b"error", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["content", "error"] | None: ... + +global___TextStreamReaderReadAllCallback = TextStreamReaderReadAllCallback + +@typing.final +class TextStreamReaderEvent(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + CHUNK_RECEIVED_FIELD_NUMBER: builtins.int + EOS_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + @property + def chunk_received(self) -> global___TextStreamReaderChunkReceived: ... + @property + def eos(self) -> global___TextStreamReaderEOS: ... + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + chunk_received: global___TextStreamReaderChunkReceived | None = ..., + eos: global___TextStreamReaderEOS | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chunk_received", b"chunk_received", "detail", b"detail", "eos", b"eos", "reader_handle", b"reader_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chunk_received", b"chunk_received", "detail", b"detail", "eos", b"eos", "reader_handle", b"reader_handle"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["detail", b"detail"]) -> typing.Literal["chunk_received", "eos"] | None: ... + +global___TextStreamReaderEvent = TextStreamReaderEvent + +@typing.final +class TextStreamReaderChunkReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CONTENT_FIELD_NUMBER: builtins.int + content: builtins.str + def __init__( + self, + *, + content: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["content", b"content"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["content", b"content"]) -> None: ... + +global___TextStreamReaderChunkReceived = TextStreamReaderChunkReceived + +@typing.final +class TextStreamReaderEOS(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + ERROR_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + @property + def error(self) -> global___StreamError: ... + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Final stream attributes, including any delivered in the stream trailer.""" + + def __init__( + self, + *, + error: global___StreamError | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "error", b"error"]) -> None: ... + +global___TextStreamReaderEOS = TextStreamReaderEOS + +@typing.final +class OwnedByteStreamReader(google.protobuf.message.Message): + """MARK: - Byte stream reader + + A reader for an incoming stream. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___ByteStreamInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___ByteStreamInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedByteStreamReader = OwnedByteStreamReader + +@typing.final +class ByteStreamReaderReadIncrementalRequest(google.protobuf.message.Message): + """Reads an incoming byte stream incrementally.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reader_handle", b"reader_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reader_handle", b"reader_handle"]) -> None: ... + +global___ByteStreamReaderReadIncrementalRequest = ByteStreamReaderReadIncrementalRequest + +@typing.final +class ByteStreamReaderReadIncrementalResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___ByteStreamReaderReadIncrementalResponse = ByteStreamReaderReadIncrementalResponse + +@typing.final +class ByteStreamReaderReadAllRequest(google.protobuf.message.Message): + """Reads an incoming byte stream in its entirety.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + request_async_id: builtins.int + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> None: ... + +global___ByteStreamReaderReadAllRequest = ByteStreamReaderReadAllRequest + +@typing.final +class ByteStreamReaderReadAllResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___ByteStreamReaderReadAllResponse = ByteStreamReaderReadAllResponse + +@typing.final +class ByteStreamReaderReadAllCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + CONTENT_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + content: builtins.bytes + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + content: builtins.bytes | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "content", b"content", "error", b"error", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "content", b"content", "error", b"error", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["content", "error"] | None: ... + +global___ByteStreamReaderReadAllCallback = ByteStreamReaderReadAllCallback + +@typing.final +class ByteStreamReaderWriteToFileRequest(google.protobuf.message.Message): + """Writes data from an incoming stream to a file as it arrives.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + DIRECTORY_FIELD_NUMBER: builtins.int + NAME_OVERRIDE_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + request_async_id: builtins.int + directory: builtins.str + """Directory to write the file in (must be writable by the current process). + If not provided, the file will be written to the system's temp directory. + """ + name_override: builtins.str + """Name to use for the written file. + If not provided, the file's name and extension will be inferred from + the stream's info. + """ + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., + directory: builtins.str | None = ..., + name_override: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["directory", b"directory", "name_override", b"name_override", "reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["directory", b"directory", "name_override", b"name_override", "reader_handle", b"reader_handle", "request_async_id", b"request_async_id"]) -> None: ... + +global___ByteStreamReaderWriteToFileRequest = ByteStreamReaderWriteToFileRequest + +@typing.final +class ByteStreamReaderWriteToFileResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___ByteStreamReaderWriteToFileResponse = ByteStreamReaderWriteToFileResponse + +@typing.final +class ByteStreamReaderWriteToFileCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + FILE_PATH_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + file_path: builtins.str + """Path the file was written to.""" + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + file_path: builtins.str | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "file_path", b"file_path", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "file_path", b"file_path", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["file_path", "error"] | None: ... + +global___ByteStreamReaderWriteToFileCallback = ByteStreamReaderWriteToFileCallback + +@typing.final +class ByteStreamReaderEvent(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_HANDLE_FIELD_NUMBER: builtins.int + CHUNK_RECEIVED_FIELD_NUMBER: builtins.int + EOS_FIELD_NUMBER: builtins.int + reader_handle: builtins.int + @property + def chunk_received(self) -> global___ByteStreamReaderChunkReceived: ... + @property + def eos(self) -> global___ByteStreamReaderEOS: ... + def __init__( + self, + *, + reader_handle: builtins.int | None = ..., + chunk_received: global___ByteStreamReaderChunkReceived | None = ..., + eos: global___ByteStreamReaderEOS | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chunk_received", b"chunk_received", "detail", b"detail", "eos", b"eos", "reader_handle", b"reader_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chunk_received", b"chunk_received", "detail", b"detail", "eos", b"eos", "reader_handle", b"reader_handle"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["detail", b"detail"]) -> typing.Literal["chunk_received", "eos"] | None: ... + +global___ByteStreamReaderEvent = ByteStreamReaderEvent + +@typing.final +class ByteStreamReaderChunkReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CONTENT_FIELD_NUMBER: builtins.int + content: builtins.bytes + def __init__( + self, + *, + content: builtins.bytes | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["content", b"content"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["content", b"content"]) -> None: ... + +global___ByteStreamReaderChunkReceived = ByteStreamReaderChunkReceived + +@typing.final +class ByteStreamReaderEOS(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + ERROR_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + @property + def error(self) -> global___StreamError: ... + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Final stream attributes, including any delivered in the stream trailer.""" + + def __init__( + self, + *, + error: global___StreamError | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "error", b"error"]) -> None: ... + +global___ByteStreamReaderEOS = ByteStreamReaderEOS + +@typing.final +class StreamSendFileRequest(google.protobuf.message.Message): + """MARK: - Send file + + Sends the contents of a file over a data stream. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + FILE_PATH_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + file_path: builtins.str + """Path of the file to send (must be readable by the current process).""" + request_async_id: builtins.int + @property + def options(self) -> global___StreamByteOptions: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___StreamByteOptions | None = ..., + file_path: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["file_path", b"file_path", "local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["file_path", b"file_path", "local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> None: ... + +global___StreamSendFileRequest = StreamSendFileRequest + +@typing.final +class StreamSendFileResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___StreamSendFileResponse = StreamSendFileResponse + +@typing.final +class StreamSendFileCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def info(self) -> global___ByteStreamInfo: ... + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + info: global___ByteStreamInfo | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["info", "error"] | None: ... + +global___StreamSendFileCallback = StreamSendFileCallback + +@typing.final +class StreamSendBytesRequest(google.protobuf.message.Message): + """MARK: - Send bytes + + Sends bytes over a data stream. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + BYTES_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + bytes: builtins.bytes + """Bytes to send.""" + request_async_id: builtins.int + @property + def options(self) -> global___StreamByteOptions: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___StreamByteOptions | None = ..., + bytes: builtins.bytes | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["bytes", b"bytes", "local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bytes", b"bytes", "local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> None: ... + +global___StreamSendBytesRequest = StreamSendBytesRequest + +@typing.final +class StreamSendBytesResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___StreamSendBytesResponse = StreamSendBytesResponse + +@typing.final +class StreamSendBytesCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def info(self) -> global___ByteStreamInfo: ... + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + info: global___ByteStreamInfo | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["info", "error"] | None: ... + +global___StreamSendBytesCallback = StreamSendBytesCallback + +@typing.final +class StreamSendTextRequest(google.protobuf.message.Message): + """MARK: - Send text + + Sends text over a data stream. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + TEXT_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + text: builtins.str + """Text to send.""" + request_async_id: builtins.int + @property + def options(self) -> global___StreamTextOptions: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___StreamTextOptions | None = ..., + text: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id", "text", b"text"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id", "text", b"text"]) -> None: ... + +global___StreamSendTextRequest = StreamSendTextRequest + +@typing.final +class StreamSendTextResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___StreamSendTextResponse = StreamSendTextResponse + +@typing.final +class StreamSendTextCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def info(self) -> global___TextStreamInfo: ... + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + info: global___TextStreamInfo | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "info", b"info", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["info", "error"] | None: ... + +global___StreamSendTextCallback = StreamSendTextCallback + +@typing.final +class OwnedByteStreamWriter(google.protobuf.message.Message): + """MARK: - Byte stream writer""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___ByteStreamInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___ByteStreamInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedByteStreamWriter = OwnedByteStreamWriter + +@typing.final +class ByteStreamOpenRequest(google.protobuf.message.Message): + """Opens an outgoing stream. + Call must be balanced with a StreamCloseRequest. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + request_async_id: builtins.int + @property + def options(self) -> global___StreamByteOptions: + """Options to use for opening the stream.""" + + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___StreamByteOptions | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> None: ... + +global___ByteStreamOpenRequest = ByteStreamOpenRequest + +@typing.final +class ByteStreamOpenResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___ByteStreamOpenResponse = ByteStreamOpenResponse + +@typing.final +class ByteStreamOpenCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + WRITER_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def writer(self) -> global___OwnedByteStreamWriter: ... + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + writer: global___OwnedByteStreamWriter | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "writer", b"writer"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "writer", b"writer"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["writer", "error"] | None: ... + +global___ByteStreamOpenCallback = ByteStreamOpenCallback + +@typing.final +class ByteStreamWriterWriteRequest(google.protobuf.message.Message): + """Writes data to a stream writer.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + WRITER_HANDLE_FIELD_NUMBER: builtins.int + BYTES_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + writer_handle: builtins.int + bytes: builtins.bytes + request_async_id: builtins.int + def __init__( + self, + *, + writer_handle: builtins.int | None = ..., + bytes: builtins.bytes | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["bytes", b"bytes", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bytes", b"bytes", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> None: ... + +global___ByteStreamWriterWriteRequest = ByteStreamWriterWriteRequest + +@typing.final +class ByteStreamWriterWriteResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___ByteStreamWriterWriteResponse = ByteStreamWriterWriteResponse + +@typing.final +class ByteStreamWriterWriteCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___ByteStreamWriterWriteCallback = ByteStreamWriterWriteCallback + +@typing.final +class ByteStreamWriterCloseRequest(google.protobuf.message.Message): + """Closes a stream writer.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + WRITER_HANDLE_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + writer_handle: builtins.int + reason: builtins.str + request_async_id: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Attributes to attach to the stream trailer.""" + + def __init__( + self, + *, + writer_handle: builtins.int | None = ..., + reason: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reason", b"reason", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "reason", b"reason", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> None: ... + +global___ByteStreamWriterCloseRequest = ByteStreamWriterCloseRequest + +@typing.final +class ByteStreamWriterCloseResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___ByteStreamWriterCloseResponse = ByteStreamWriterCloseResponse + +@typing.final +class ByteStreamWriterCloseCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___ByteStreamWriterCloseCallback = ByteStreamWriterCloseCallback + +@typing.final +class OwnedTextStreamWriter(google.protobuf.message.Message): + """MARK: - Text stream writer""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___TextStreamInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___TextStreamInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedTextStreamWriter = OwnedTextStreamWriter + +@typing.final +class TextStreamOpenRequest(google.protobuf.message.Message): + """Opens an outgoing text stream. + Call must be balanced with a TextStreamCloseRequest. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + request_async_id: builtins.int + @property + def options(self) -> global___StreamTextOptions: + """Options to use for opening the stream.""" + + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___StreamTextOptions | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> None: ... + +global___TextStreamOpenRequest = TextStreamOpenRequest + +@typing.final +class TextStreamOpenResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___TextStreamOpenResponse = TextStreamOpenResponse + +@typing.final +class TextStreamOpenCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + WRITER_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def writer(self) -> global___OwnedTextStreamWriter: ... + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + writer: global___OwnedTextStreamWriter | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "writer", b"writer"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "writer", b"writer"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["writer", "error"] | None: ... + +global___TextStreamOpenCallback = TextStreamOpenCallback + +@typing.final +class TextStreamWriterWriteRequest(google.protobuf.message.Message): + """Writes text to a text stream writer.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + WRITER_HANDLE_FIELD_NUMBER: builtins.int + TEXT_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + writer_handle: builtins.int + text: builtins.str + request_async_id: builtins.int + def __init__( + self, + *, + writer_handle: builtins.int | None = ..., + text: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "text", b"text", "writer_handle", b"writer_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "text", b"text", "writer_handle", b"writer_handle"]) -> None: ... + +global___TextStreamWriterWriteRequest = TextStreamWriterWriteRequest + +@typing.final +class TextStreamWriterWriteResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___TextStreamWriterWriteResponse = TextStreamWriterWriteResponse + +@typing.final +class TextStreamWriterWriteCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___TextStreamWriterWriteCallback = TextStreamWriterWriteCallback + +@typing.final +class TextStreamWriterCloseRequest(google.protobuf.message.Message): + """Closes a text stream writer.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + WRITER_HANDLE_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + writer_handle: builtins.int + reason: builtins.str + request_async_id: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """Attributes to attach to the stream trailer.""" + + def __init__( + self, + *, + writer_handle: builtins.int | None = ..., + reason: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reason", b"reason", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "reason", b"reason", "request_async_id", b"request_async_id", "writer_handle", b"writer_handle"]) -> None: ... + +global___TextStreamWriterCloseRequest = TextStreamWriterCloseRequest + +@typing.final +class TextStreamWriterCloseResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___TextStreamWriterCloseResponse = TextStreamWriterCloseResponse + +@typing.final +class TextStreamWriterCloseCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def error(self) -> global___StreamError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: global___StreamError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___TextStreamWriterCloseCallback = TextStreamWriterCloseCallback + +@typing.final +class TextStreamInfo(google.protobuf.message.Message): + """Contains a subset of the fields from the stream header. + Protocol-level fields not relevant to the FFI client are omitted (e.g. encryption info). + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _OperationType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _OperationTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[TextStreamInfo._OperationType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CREATE: TextStreamInfo._OperationType.ValueType # 0 + UPDATE: TextStreamInfo._OperationType.ValueType # 1 + DELETE: TextStreamInfo._OperationType.ValueType # 2 + REACTION: TextStreamInfo._OperationType.ValueType # 3 + + class OperationType(_OperationType, metaclass=_OperationTypeEnumTypeWrapper): ... + CREATE: TextStreamInfo.OperationType.ValueType # 0 + UPDATE: TextStreamInfo.OperationType.ValueType # 1 + DELETE: TextStreamInfo.OperationType.ValueType # 2 + REACTION: TextStreamInfo.OperationType.ValueType # 3 + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + STREAM_ID_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + TOPIC_FIELD_NUMBER: builtins.int + TOTAL_LENGTH_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + OPERATION_TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + REPLY_TO_STREAM_ID_FIELD_NUMBER: builtins.int + ATTACHED_STREAM_IDS_FIELD_NUMBER: builtins.int + GENERATED_FIELD_NUMBER: builtins.int + ENCRYPTION_TYPE_FIELD_NUMBER: builtins.int + stream_id: builtins.str + """unique identifier for this data stream""" + timestamp: builtins.int + """using int64 for Unix timestamp""" + mime_type: builtins.str + topic: builtins.str + total_length: builtins.int + """only populated for finite streams, if it's a stream of unknown size this stays empty""" + operation_type: global___TextStreamInfo.OperationType.ValueType + version: builtins.int + """Optional: Version for updates/edits""" + reply_to_stream_id: builtins.str + """Optional: Reply to specific message""" + generated: builtins.bool + """true if the text has been generated by an agent from a participant's audio transcription""" + encryption_type: e2ee_pb2.EncryptionType.ValueType + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """user defined attributes map that can carry additional info""" + + @property + def attached_stream_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """file attachments for text streams""" + + def __init__( + self, + *, + stream_id: builtins.str | None = ..., + timestamp: builtins.int | None = ..., + mime_type: builtins.str | None = ..., + topic: builtins.str | None = ..., + total_length: builtins.int | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + operation_type: global___TextStreamInfo.OperationType.ValueType | None = ..., + version: builtins.int | None = ..., + reply_to_stream_id: builtins.str | None = ..., + attached_stream_ids: collections.abc.Iterable[builtins.str] | None = ..., + generated: builtins.bool | None = ..., + encryption_type: e2ee_pb2.EncryptionType.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["encryption_type", b"encryption_type", "generated", b"generated", "mime_type", b"mime_type", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "stream_id", b"stream_id", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length", "version", b"version"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attached_stream_ids", b"attached_stream_ids", "attributes", b"attributes", "encryption_type", b"encryption_type", "generated", b"generated", "mime_type", b"mime_type", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "stream_id", b"stream_id", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length", "version", b"version"]) -> None: ... + +global___TextStreamInfo = TextStreamInfo + +@typing.final +class ByteStreamInfo(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + STREAM_ID_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + TOPIC_FIELD_NUMBER: builtins.int + TOTAL_LENGTH_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + ENCRYPTION_TYPE_FIELD_NUMBER: builtins.int + stream_id: builtins.str + """unique identifier for this data stream""" + timestamp: builtins.int + """using int64 for Unix timestamp""" + mime_type: builtins.str + topic: builtins.str + total_length: builtins.int + """only populated for finite streams, if it's a stream of unknown size this stays empty""" + name: builtins.str + encryption_type: e2ee_pb2.EncryptionType.ValueType + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """user defined attributes map that can carry additional info""" + + def __init__( + self, + *, + stream_id: builtins.str | None = ..., + timestamp: builtins.int | None = ..., + mime_type: builtins.str | None = ..., + topic: builtins.str | None = ..., + total_length: builtins.int | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + name: builtins.str | None = ..., + encryption_type: e2ee_pb2.EncryptionType.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["encryption_type", b"encryption_type", "mime_type", b"mime_type", "name", b"name", "stream_id", b"stream_id", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "encryption_type", b"encryption_type", "mime_type", b"mime_type", "name", b"name", "stream_id", b"stream_id", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length"]) -> None: ... + +global___ByteStreamInfo = ByteStreamInfo + +@typing.final +class StreamTextOptions(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + TOPIC_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + OPERATION_TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + REPLY_TO_STREAM_ID_FIELD_NUMBER: builtins.int + ATTACHED_STREAM_IDS_FIELD_NUMBER: builtins.int + GENERATED_FIELD_NUMBER: builtins.int + COMPRESS_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + topic: builtins.str + id: builtins.str + operation_type: global___TextStreamInfo.OperationType.ValueType + version: builtins.int + reply_to_stream_id: builtins.str + generated: builtins.bool + compress: builtins.bool + sender_identity: builtins.str + """Identity the stream's packets are attributed to; requires permission to impersonate.""" + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def attached_stream_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + topic: builtins.str | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + id: builtins.str | None = ..., + operation_type: global___TextStreamInfo.OperationType.ValueType | None = ..., + version: builtins.int | None = ..., + reply_to_stream_id: builtins.str | None = ..., + attached_stream_ids: collections.abc.Iterable[builtins.str] | None = ..., + generated: builtins.bool | None = ..., + compress: builtins.bool | None = ..., + sender_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["compress", b"compress", "generated", b"generated", "id", b"id", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "sender_identity", b"sender_identity", "topic", b"topic", "version", b"version"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attached_stream_ids", b"attached_stream_ids", "attributes", b"attributes", "compress", b"compress", "destination_identities", b"destination_identities", "generated", b"generated", "id", b"id", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "sender_identity", b"sender_identity", "topic", b"topic", "version", b"version"]) -> None: ... + +global___StreamTextOptions = StreamTextOptions + +@typing.final +class StreamByteOptions(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + TOPIC_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + NAME_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + TOTAL_LENGTH_FIELD_NUMBER: builtins.int + COMPRESS_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + topic: builtins.str + id: builtins.str + name: builtins.str + mime_type: builtins.str + total_length: builtins.int + compress: builtins.bool + sender_identity: builtins.str + """Identity the stream's packets are attributed to; requires permission to impersonate.""" + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + topic: builtins.str | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + id: builtins.str | None = ..., + name: builtins.str | None = ..., + mime_type: builtins.str | None = ..., + total_length: builtins.int | None = ..., + compress: builtins.bool | None = ..., + sender_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["compress", b"compress", "id", b"id", "mime_type", b"mime_type", "name", b"name", "sender_identity", b"sender_identity", "topic", b"topic", "total_length", b"total_length"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "compress", b"compress", "destination_identities", b"destination_identities", "id", b"id", "mime_type", b"mime_type", "name", b"name", "sender_identity", b"sender_identity", "topic", b"topic", "total_length", b"total_length"]) -> None: ... + +global___StreamByteOptions = StreamByteOptions + +@typing.final +class StreamError(google.protobuf.message.Message): + """Error pertaining to a stream.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + DESCRIPTION_FIELD_NUMBER: builtins.int + description: builtins.str + """TODO(ladvoc): make this an enum.""" + def __init__( + self, + *, + description: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["description", b"description"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["description", b"description"]) -> None: ... + +global___StreamError = StreamError diff --git a/livekit-rtc/livekit/rtc/_proto/data_track_pb2.py b/livekit-rtc/livekit/rtc/_proto/data_track_pb2.py new file mode 100644 index 00000000..09a45f67 --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/data_track_pb2.py @@ -0,0 +1,120 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: data_track.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from . import handle_pb2 as handle__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x10\x64\x61ta_track.proto\x12\rlivekit.proto\x1a\x0chandle.proto\"\xae\x01\n\rDataTrackInfo\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x0b\n\x03sid\x18\x02 \x02(\t\x12\x11\n\tuses_e2ee\x18\x03 \x02(\x08\x12\x30\n\x06schema\x18\x04 \x01(\x0b\x32 .livekit.proto.DataTrackSchemaId\x12=\n\x0e\x66rame_encoding\x18\x05 \x01(\x0b\x32%.livekit.proto.DataTrackFrameEncoding\"9\n\x0e\x44\x61taTrackFrame\x12\x0f\n\x07payload\x18\x01 \x02(\x0c\x12\x16\n\x0euser_timestamp\x18\x02 \x01(\x04\"R\n\x0e\x44\x61taTrackError\x12/\n\x04\x63ode\x18\x01 \x02(\x0e\x32!.livekit.proto.DataTrackErrorCode\x12\x0f\n\x07message\x18\x02 \x02(\t\"`\n\x15PublishDataTrackError\x12\x36\n\x04\x63ode\x18\x01 \x02(\x0e\x32(.livekit.proto.PublishDataTrackErrorCode\x12\x0f\n\x07message\x18\x02 \x02(\t\"j\n\x1aLocalDataTrackTryPushError\x12;\n\x04\x63ode\x18\x01 \x02(\x0e\x32-.livekit.proto.LocalDataTrackTryPushErrorCode\x12\x0f\n\x07message\x18\x02 \x02(\t\"d\n\x17SubscribeDataTrackError\x12\x38\n\x04\x63ode\x18\x01 \x02(\x0e\x32*.livekit.proto.SubscribeDataTrackErrorCode\x12\x0f\n\x07message\x18\x02 \x02(\t\"\x91\x01\n\x10\x44\x61taTrackOptions\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x30\n\x06schema\x18\x02 \x01(\x0b\x32 .livekit.proto.DataTrackSchemaId\x12=\n\x0e\x66rame_encoding\x18\x03 \x01(\x0b\x32%.livekit.proto.DataTrackFrameEncoding\"\x87\x01\n\x17PublishDataTrackRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x30\n\x07options\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.DataTrackOptions\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\",\n\x18PublishDataTrackResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\xa2\x01\n\x18PublishDataTrackCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x33\n\x05track\x18\x02 \x01(\x0b\x32\".livekit.proto.OwnedLocalDataTrackH\x00\x12\x35\n\x05\x65rror\x18\x03 \x01(\x0b\x32$.livekit.proto.PublishDataTrackErrorH\x00\x42\x08\n\x06result\"p\n\x13OwnedLocalDataTrack\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12*\n\x04info\x18\x02 \x02(\x0b\x32\x1c.livekit.proto.DataTrackInfo\"b\n\x1cLocalDataTrackTryPushRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12,\n\x05\x66rame\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.DataTrackFrame\"Y\n\x1dLocalDataTrackTryPushResponse\x12\x38\n\x05\x65rror\x18\x02 \x01(\x0b\x32).livekit.proto.LocalDataTrackTryPushError\"8\n LocalDataTrackIsPublishedRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\"9\n!LocalDataTrackIsPublishedResponse\x12\x14\n\x0cis_published\x18\x01 \x02(\x08\"6\n\x1eLocalDataTrackUnpublishRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\"!\n\x1fLocalDataTrackUnpublishResponse\"\x8d\x01\n\x14OwnedRemoteDataTrack\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12*\n\x04info\x18\x02 \x02(\x0b\x32\x1c.livekit.proto.DataTrackInfo\x12\x1a\n\x12publisher_identity\x18\x03 \x02(\t\"E\n\x14OwnedDataTrackStream\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\"0\n\x19\x44\x61taTrackSubscribeOptions\x12\x13\n\x0b\x62uffer_size\x18\x01 \x01(\r\"9\n!RemoteDataTrackIsPublishedRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\":\n\"RemoteDataTrackIsPublishedResponse\x12\x14\n\x0cis_published\x18\x01 \x02(\x08\"<\n\x1eRemoteDataTrackPipelineOptions\x12\x1a\n\x12max_partial_frames\x18\x01 \x01(\r\"\x80\x01\n(RemoteDataTrackSetPipelineOptionsRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12>\n\x07options\x18\x02 \x02(\x0b\x32-.livekit.proto.RemoteDataTrackPipelineOptions\"+\n)RemoteDataTrackSetPipelineOptionsResponse\"l\n\x19SubscribeDataTrackRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12\x39\n\x07options\x18\x02 \x02(\x0b\x32(.livekit.proto.DataTrackSubscribeOptions\"Q\n\x1aSubscribeDataTrackResponse\x12\x33\n\x06stream\x18\x01 \x02(\x0b\x32#.livekit.proto.OwnedDataTrackStream\"3\n\x1a\x44\x61taTrackStreamReadRequest\x12\x15\n\rstream_handle\x18\x01 \x02(\x04\"S\n\x1b\x44\x61taTrackStreamReadResponse\x12\x34\n\teos_event\x18\x01 \x01(\x0b\x32!.livekit.proto.DataTrackStreamEOS\"\xb0\x01\n\x14\x44\x61taTrackStreamEvent\x12\x15\n\rstream_handle\x18\x01 \x02(\x04\x12\x45\n\x0e\x66rame_received\x18\x02 \x01(\x0b\x32+.livekit.proto.DataTrackStreamFrameReceivedH\x00\x12\x30\n\x03\x65os\x18\x03 \x01(\x0b\x32!.livekit.proto.DataTrackStreamEOSH\x00\x42\x08\n\x06\x64\x65tail\"L\n\x1c\x44\x61taTrackStreamFrameReceived\x12,\n\x05\x66rame\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.DataTrackFrame\"K\n\x12\x44\x61taTrackStreamEOS\x12\x35\n\x05\x65rror\x18\x01 \x01(\x0b\x32&.livekit.proto.SubscribeDataTrackError\"\xf8\x03\n\x17\x44\x61taTrackSchemaEncoding\x12T\n\nwell_known\x18\x01 \x01(\x0e\x32>.livekit.proto.DataTrackSchemaEncoding.WellKnownSchemaEncodingH\x00\x12\x10\n\x06\x63ustom\x18\x02 \x01(\tH\x00\"\xe8\x02\n\x17WellKnownSchemaEncoding\x12*\n&WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED\x10\x00\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF\x10\x01\x12)\n%WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER\x10\x02\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG\x10\x03\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG\x10\x04\x12\'\n#WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL\x10\x05\x12&\n\"WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL\x10\x06\x12*\n&WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA\x10\x07\x42\n\n\x08\x65ncoding\"\xd8\x03\n\x16\x44\x61taTrackFrameEncoding\x12R\n\nwell_known\x18\x01 \x01(\x0e\x32<.livekit.proto.DataTrackFrameEncoding.WellKnownFrameEncodingH\x00\x12\x10\n\x06\x63ustom\x18\x02 \x01(\tH\x00\"\xcb\x02\n\x16WellKnownFrameEncoding\x12)\n%WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED\x10\x00\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_ROS1\x10\x01\x12!\n\x1dWELL_KNOWN_FRAME_ENCODING_CDR\x10\x02\x12&\n\"WELL_KNOWN_FRAME_ENCODING_PROTOBUF\x10\x03\x12(\n$WELL_KNOWN_FRAME_ENCODING_FLATBUFFER\x10\x04\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_CBOR\x10\x05\x12%\n!WELL_KNOWN_FRAME_ENCODING_MSGPACK\x10\x06\x12\"\n\x1eWELL_KNOWN_FRAME_ENCODING_JSON\x10\x07\x42\n\n\x08\x65ncoding\"[\n\x11\x44\x61taTrackSchemaId\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x38\n\x08\x65ncoding\x18\x02 \x01(\x0b\x32&.livekit.proto.DataTrackSchemaEncoding\"\x9a\x01\n\x13\x44\x65\x66ineSchemaRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x33\n\tschema_id\x18\x02 \x02(\x0b\x32 .livekit.proto.DataTrackSchemaId\x12\x12\n\ndefinition\x18\x03 \x02(\t\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"(\n\x14\x44\x65\x66ineSchemaResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"7\n\x14\x44\x65\x66ineSchemaCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\xa1\x01\n\x10GetSchemaRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x33\n\tschema_id\x18\x02 \x02(\x0b\x32 .livekit.proto.DataTrackSchemaId\x12\x1c\n\x14participant_identity\x18\x03 \x02(\t\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"%\n\x11GetSchemaResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"H\n\x11GetSchemaCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x12\n\ndefinition\x18\x02 \x01(\t\x12\r\n\x05\x65rror\x18\x03 \x01(\t*\x87\x03\n\x12\x44\x61taTrackErrorCode\x12!\n\x1d\x44\x41TA_TRACK_ERROR_CODE_UNKNOWN\x10\x00\x12(\n$DATA_TRACK_ERROR_CODE_INVALID_HANDLE\x10\x01\x12.\n*DATA_TRACK_ERROR_CODE_DUPLICATE_TRACK_NAME\x10\x02\x12+\n\'DATA_TRACK_ERROR_CODE_TRACK_UNPUBLISHED\x10\x03\x12%\n!DATA_TRACK_ERROR_CODE_BUFFER_FULL\x10\x04\x12-\n)DATA_TRACK_ERROR_CODE_SUBSCRIPTION_CLOSED\x10\x05\x12#\n\x1f\x44\x41TA_TRACK_ERROR_CODE_CANCELLED\x10\x06\x12(\n$DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR\x10\x07\x12\"\n\x1e\x44\x41TA_TRACK_ERROR_CODE_INTERNAL\x10\x08*\xa5\x04\n\x19PublishDataTrackErrorCode\x12)\n%PUBLISH_DATA_TRACK_ERROR_CODE_UNKNOWN\x10\x00\x12\x30\n,PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_HANDLE\x10\x01\x12\x30\n,PUBLISH_DATA_TRACK_ERROR_CODE_DUPLICATE_NAME\x10\x02\x12)\n%PUBLISH_DATA_TRACK_ERROR_CODE_TIMEOUT\x10\x03\x12.\n*PUBLISH_DATA_TRACK_ERROR_CODE_DISCONNECTED\x10\x04\x12-\n)PUBLISH_DATA_TRACK_ERROR_CODE_NOT_ALLOWED\x10\x05\x12.\n*PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_NAME\x10\x06\x12/\n+PUBLISH_DATA_TRACK_ERROR_CODE_LIMIT_REACHED\x10\x07\x12\x30\n,PUBLISH_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR\x10\x08\x12*\n&PUBLISH_DATA_TRACK_ERROR_CODE_INTERNAL\x10\t\x12\x30\n,PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_SCHEMA\x10\n*\xaf\x02\n\x1eLocalDataTrackTryPushErrorCode\x12\x30\n,LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_UNKNOWN\x10\x00\x12\x37\n3LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INVALID_HANDLE\x10\x01\x12:\n6LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_TRACK_UNPUBLISHED\x10\x02\x12\x33\n/LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_QUEUE_FULL\x10\x03\x12\x31\n-LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INTERNAL\x10\x04*\xf0\x02\n\x1bSubscribeDataTrackErrorCode\x12+\n\'SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNKNOWN\x10\x00\x12\x32\n.SUBSCRIBE_DATA_TRACK_ERROR_CODE_INVALID_HANDLE\x10\x01\x12/\n+SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNPUBLISHED\x10\x02\x12+\n\'SUBSCRIBE_DATA_TRACK_ERROR_CODE_TIMEOUT\x10\x03\x12\x30\n,SUBSCRIBE_DATA_TRACK_ERROR_CODE_DISCONNECTED\x10\x04\x12\x32\n.SUBSCRIBE_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR\x10\x05\x12,\n(SUBSCRIBE_DATA_TRACK_ERROR_CODE_INTERNAL\x10\x06\x42\x10\xaa\x02\rLiveKit.Proto') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'data_track_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' + _globals['_DATATRACKERRORCODE']._serialized_start=4583 + _globals['_DATATRACKERRORCODE']._serialized_end=4974 + _globals['_PUBLISHDATATRACKERRORCODE']._serialized_start=4977 + _globals['_PUBLISHDATATRACKERRORCODE']._serialized_end=5526 + _globals['_LOCALDATATRACKTRYPUSHERRORCODE']._serialized_start=5529 + _globals['_LOCALDATATRACKTRYPUSHERRORCODE']._serialized_end=5832 + _globals['_SUBSCRIBEDATATRACKERRORCODE']._serialized_start=5835 + _globals['_SUBSCRIBEDATATRACKERRORCODE']._serialized_end=6203 + _globals['_DATATRACKINFO']._serialized_start=50 + _globals['_DATATRACKINFO']._serialized_end=224 + _globals['_DATATRACKFRAME']._serialized_start=226 + _globals['_DATATRACKFRAME']._serialized_end=283 + _globals['_DATATRACKERROR']._serialized_start=285 + _globals['_DATATRACKERROR']._serialized_end=367 + _globals['_PUBLISHDATATRACKERROR']._serialized_start=369 + _globals['_PUBLISHDATATRACKERROR']._serialized_end=465 + _globals['_LOCALDATATRACKTRYPUSHERROR']._serialized_start=467 + _globals['_LOCALDATATRACKTRYPUSHERROR']._serialized_end=573 + _globals['_SUBSCRIBEDATATRACKERROR']._serialized_start=575 + _globals['_SUBSCRIBEDATATRACKERROR']._serialized_end=675 + _globals['_DATATRACKOPTIONS']._serialized_start=678 + _globals['_DATATRACKOPTIONS']._serialized_end=823 + _globals['_PUBLISHDATATRACKREQUEST']._serialized_start=826 + _globals['_PUBLISHDATATRACKREQUEST']._serialized_end=961 + _globals['_PUBLISHDATATRACKRESPONSE']._serialized_start=963 + _globals['_PUBLISHDATATRACKRESPONSE']._serialized_end=1007 + _globals['_PUBLISHDATATRACKCALLBACK']._serialized_start=1010 + _globals['_PUBLISHDATATRACKCALLBACK']._serialized_end=1172 + _globals['_OWNEDLOCALDATATRACK']._serialized_start=1174 + _globals['_OWNEDLOCALDATATRACK']._serialized_end=1286 + _globals['_LOCALDATATRACKTRYPUSHREQUEST']._serialized_start=1288 + _globals['_LOCALDATATRACKTRYPUSHREQUEST']._serialized_end=1386 + _globals['_LOCALDATATRACKTRYPUSHRESPONSE']._serialized_start=1388 + _globals['_LOCALDATATRACKTRYPUSHRESPONSE']._serialized_end=1477 + _globals['_LOCALDATATRACKISPUBLISHEDREQUEST']._serialized_start=1479 + _globals['_LOCALDATATRACKISPUBLISHEDREQUEST']._serialized_end=1535 + _globals['_LOCALDATATRACKISPUBLISHEDRESPONSE']._serialized_start=1537 + _globals['_LOCALDATATRACKISPUBLISHEDRESPONSE']._serialized_end=1594 + _globals['_LOCALDATATRACKUNPUBLISHREQUEST']._serialized_start=1596 + _globals['_LOCALDATATRACKUNPUBLISHREQUEST']._serialized_end=1650 + _globals['_LOCALDATATRACKUNPUBLISHRESPONSE']._serialized_start=1652 + _globals['_LOCALDATATRACKUNPUBLISHRESPONSE']._serialized_end=1685 + _globals['_OWNEDREMOTEDATATRACK']._serialized_start=1688 + _globals['_OWNEDREMOTEDATATRACK']._serialized_end=1829 + _globals['_OWNEDDATATRACKSTREAM']._serialized_start=1831 + _globals['_OWNEDDATATRACKSTREAM']._serialized_end=1900 + _globals['_DATATRACKSUBSCRIBEOPTIONS']._serialized_start=1902 + _globals['_DATATRACKSUBSCRIBEOPTIONS']._serialized_end=1950 + _globals['_REMOTEDATATRACKISPUBLISHEDREQUEST']._serialized_start=1952 + _globals['_REMOTEDATATRACKISPUBLISHEDREQUEST']._serialized_end=2009 + _globals['_REMOTEDATATRACKISPUBLISHEDRESPONSE']._serialized_start=2011 + _globals['_REMOTEDATATRACKISPUBLISHEDRESPONSE']._serialized_end=2069 + _globals['_REMOTEDATATRACKPIPELINEOPTIONS']._serialized_start=2071 + _globals['_REMOTEDATATRACKPIPELINEOPTIONS']._serialized_end=2131 + _globals['_REMOTEDATATRACKSETPIPELINEOPTIONSREQUEST']._serialized_start=2134 + _globals['_REMOTEDATATRACKSETPIPELINEOPTIONSREQUEST']._serialized_end=2262 + _globals['_REMOTEDATATRACKSETPIPELINEOPTIONSRESPONSE']._serialized_start=2264 + _globals['_REMOTEDATATRACKSETPIPELINEOPTIONSRESPONSE']._serialized_end=2307 + _globals['_SUBSCRIBEDATATRACKREQUEST']._serialized_start=2309 + _globals['_SUBSCRIBEDATATRACKREQUEST']._serialized_end=2417 + _globals['_SUBSCRIBEDATATRACKRESPONSE']._serialized_start=2419 + _globals['_SUBSCRIBEDATATRACKRESPONSE']._serialized_end=2500 + _globals['_DATATRACKSTREAMREADREQUEST']._serialized_start=2502 + _globals['_DATATRACKSTREAMREADREQUEST']._serialized_end=2553 + _globals['_DATATRACKSTREAMREADRESPONSE']._serialized_start=2555 + _globals['_DATATRACKSTREAMREADRESPONSE']._serialized_end=2638 + _globals['_DATATRACKSTREAMEVENT']._serialized_start=2641 + _globals['_DATATRACKSTREAMEVENT']._serialized_end=2817 + _globals['_DATATRACKSTREAMFRAMERECEIVED']._serialized_start=2819 + _globals['_DATATRACKSTREAMFRAMERECEIVED']._serialized_end=2895 + _globals['_DATATRACKSTREAMEOS']._serialized_start=2897 + _globals['_DATATRACKSTREAMEOS']._serialized_end=2972 + _globals['_DATATRACKSCHEMAENCODING']._serialized_start=2975 + _globals['_DATATRACKSCHEMAENCODING']._serialized_end=3479 + _globals['_DATATRACKSCHEMAENCODING_WELLKNOWNSCHEMAENCODING']._serialized_start=3107 + _globals['_DATATRACKSCHEMAENCODING_WELLKNOWNSCHEMAENCODING']._serialized_end=3467 + _globals['_DATATRACKFRAMEENCODING']._serialized_start=3482 + _globals['_DATATRACKFRAMEENCODING']._serialized_end=3954 + _globals['_DATATRACKFRAMEENCODING_WELLKNOWNFRAMEENCODING']._serialized_start=3611 + _globals['_DATATRACKFRAMEENCODING_WELLKNOWNFRAMEENCODING']._serialized_end=3942 + _globals['_DATATRACKSCHEMAID']._serialized_start=3956 + _globals['_DATATRACKSCHEMAID']._serialized_end=4047 + _globals['_DEFINESCHEMAREQUEST']._serialized_start=4050 + _globals['_DEFINESCHEMAREQUEST']._serialized_end=4204 + _globals['_DEFINESCHEMARESPONSE']._serialized_start=4206 + _globals['_DEFINESCHEMARESPONSE']._serialized_end=4246 + _globals['_DEFINESCHEMACALLBACK']._serialized_start=4248 + _globals['_DEFINESCHEMACALLBACK']._serialized_end=4303 + _globals['_GETSCHEMAREQUEST']._serialized_start=4306 + _globals['_GETSCHEMAREQUEST']._serialized_end=4467 + _globals['_GETSCHEMARESPONSE']._serialized_start=4469 + _globals['_GETSCHEMARESPONSE']._serialized_end=4506 + _globals['_GETSCHEMACALLBACK']._serialized_start=4508 + _globals['_GETSCHEMACALLBACK']._serialized_end=4580 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/data_track_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/data_track_pb2.pyi new file mode 100644 index 00000000..a61e3929 --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/data_track_pb2.pyi @@ -0,0 +1,1073 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2026 LiveKit, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +from . import handle_pb2 +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _DataTrackErrorCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DataTrackErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DataTrackErrorCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DATA_TRACK_ERROR_CODE_UNKNOWN: _DataTrackErrorCode.ValueType # 0 + DATA_TRACK_ERROR_CODE_INVALID_HANDLE: _DataTrackErrorCode.ValueType # 1 + DATA_TRACK_ERROR_CODE_DUPLICATE_TRACK_NAME: _DataTrackErrorCode.ValueType # 2 + DATA_TRACK_ERROR_CODE_TRACK_UNPUBLISHED: _DataTrackErrorCode.ValueType # 3 + DATA_TRACK_ERROR_CODE_BUFFER_FULL: _DataTrackErrorCode.ValueType # 4 + DATA_TRACK_ERROR_CODE_SUBSCRIPTION_CLOSED: _DataTrackErrorCode.ValueType # 5 + DATA_TRACK_ERROR_CODE_CANCELLED: _DataTrackErrorCode.ValueType # 6 + DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: _DataTrackErrorCode.ValueType # 7 + DATA_TRACK_ERROR_CODE_INTERNAL: _DataTrackErrorCode.ValueType # 8 + +class DataTrackErrorCode(_DataTrackErrorCode, metaclass=_DataTrackErrorCodeEnumTypeWrapper): ... + +DATA_TRACK_ERROR_CODE_UNKNOWN: DataTrackErrorCode.ValueType # 0 +DATA_TRACK_ERROR_CODE_INVALID_HANDLE: DataTrackErrorCode.ValueType # 1 +DATA_TRACK_ERROR_CODE_DUPLICATE_TRACK_NAME: DataTrackErrorCode.ValueType # 2 +DATA_TRACK_ERROR_CODE_TRACK_UNPUBLISHED: DataTrackErrorCode.ValueType # 3 +DATA_TRACK_ERROR_CODE_BUFFER_FULL: DataTrackErrorCode.ValueType # 4 +DATA_TRACK_ERROR_CODE_SUBSCRIPTION_CLOSED: DataTrackErrorCode.ValueType # 5 +DATA_TRACK_ERROR_CODE_CANCELLED: DataTrackErrorCode.ValueType # 6 +DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: DataTrackErrorCode.ValueType # 7 +DATA_TRACK_ERROR_CODE_INTERNAL: DataTrackErrorCode.ValueType # 8 +global___DataTrackErrorCode = DataTrackErrorCode + +class _PublishDataTrackErrorCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PublishDataTrackErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PublishDataTrackErrorCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PUBLISH_DATA_TRACK_ERROR_CODE_UNKNOWN: _PublishDataTrackErrorCode.ValueType # 0 + PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: _PublishDataTrackErrorCode.ValueType # 1 + PUBLISH_DATA_TRACK_ERROR_CODE_DUPLICATE_NAME: _PublishDataTrackErrorCode.ValueType # 2 + PUBLISH_DATA_TRACK_ERROR_CODE_TIMEOUT: _PublishDataTrackErrorCode.ValueType # 3 + PUBLISH_DATA_TRACK_ERROR_CODE_DISCONNECTED: _PublishDataTrackErrorCode.ValueType # 4 + PUBLISH_DATA_TRACK_ERROR_CODE_NOT_ALLOWED: _PublishDataTrackErrorCode.ValueType # 5 + PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_NAME: _PublishDataTrackErrorCode.ValueType # 6 + PUBLISH_DATA_TRACK_ERROR_CODE_LIMIT_REACHED: _PublishDataTrackErrorCode.ValueType # 7 + PUBLISH_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: _PublishDataTrackErrorCode.ValueType # 8 + PUBLISH_DATA_TRACK_ERROR_CODE_INTERNAL: _PublishDataTrackErrorCode.ValueType # 9 + PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_SCHEMA: _PublishDataTrackErrorCode.ValueType # 10 + +class PublishDataTrackErrorCode(_PublishDataTrackErrorCode, metaclass=_PublishDataTrackErrorCodeEnumTypeWrapper): ... + +PUBLISH_DATA_TRACK_ERROR_CODE_UNKNOWN: PublishDataTrackErrorCode.ValueType # 0 +PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: PublishDataTrackErrorCode.ValueType # 1 +PUBLISH_DATA_TRACK_ERROR_CODE_DUPLICATE_NAME: PublishDataTrackErrorCode.ValueType # 2 +PUBLISH_DATA_TRACK_ERROR_CODE_TIMEOUT: PublishDataTrackErrorCode.ValueType # 3 +PUBLISH_DATA_TRACK_ERROR_CODE_DISCONNECTED: PublishDataTrackErrorCode.ValueType # 4 +PUBLISH_DATA_TRACK_ERROR_CODE_NOT_ALLOWED: PublishDataTrackErrorCode.ValueType # 5 +PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_NAME: PublishDataTrackErrorCode.ValueType # 6 +PUBLISH_DATA_TRACK_ERROR_CODE_LIMIT_REACHED: PublishDataTrackErrorCode.ValueType # 7 +PUBLISH_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: PublishDataTrackErrorCode.ValueType # 8 +PUBLISH_DATA_TRACK_ERROR_CODE_INTERNAL: PublishDataTrackErrorCode.ValueType # 9 +PUBLISH_DATA_TRACK_ERROR_CODE_INVALID_SCHEMA: PublishDataTrackErrorCode.ValueType # 10 +global___PublishDataTrackErrorCode = PublishDataTrackErrorCode + +class _LocalDataTrackTryPushErrorCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _LocalDataTrackTryPushErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_LocalDataTrackTryPushErrorCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_UNKNOWN: _LocalDataTrackTryPushErrorCode.ValueType # 0 + LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INVALID_HANDLE: _LocalDataTrackTryPushErrorCode.ValueType # 1 + LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_TRACK_UNPUBLISHED: _LocalDataTrackTryPushErrorCode.ValueType # 2 + LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_QUEUE_FULL: _LocalDataTrackTryPushErrorCode.ValueType # 3 + LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INTERNAL: _LocalDataTrackTryPushErrorCode.ValueType # 4 + +class LocalDataTrackTryPushErrorCode(_LocalDataTrackTryPushErrorCode, metaclass=_LocalDataTrackTryPushErrorCodeEnumTypeWrapper): ... + +LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_UNKNOWN: LocalDataTrackTryPushErrorCode.ValueType # 0 +LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INVALID_HANDLE: LocalDataTrackTryPushErrorCode.ValueType # 1 +LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_TRACK_UNPUBLISHED: LocalDataTrackTryPushErrorCode.ValueType # 2 +LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_QUEUE_FULL: LocalDataTrackTryPushErrorCode.ValueType # 3 +LOCAL_DATA_TRACK_TRY_PUSH_ERROR_CODE_INTERNAL: LocalDataTrackTryPushErrorCode.ValueType # 4 +global___LocalDataTrackTryPushErrorCode = LocalDataTrackTryPushErrorCode + +class _SubscribeDataTrackErrorCode: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SubscribeDataTrackErrorCodeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SubscribeDataTrackErrorCode.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNKNOWN: _SubscribeDataTrackErrorCode.ValueType # 0 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: _SubscribeDataTrackErrorCode.ValueType # 1 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNPUBLISHED: _SubscribeDataTrackErrorCode.ValueType # 2 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_TIMEOUT: _SubscribeDataTrackErrorCode.ValueType # 3 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_DISCONNECTED: _SubscribeDataTrackErrorCode.ValueType # 4 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: _SubscribeDataTrackErrorCode.ValueType # 5 + SUBSCRIBE_DATA_TRACK_ERROR_CODE_INTERNAL: _SubscribeDataTrackErrorCode.ValueType # 6 + +class SubscribeDataTrackErrorCode(_SubscribeDataTrackErrorCode, metaclass=_SubscribeDataTrackErrorCodeEnumTypeWrapper): ... + +SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNKNOWN: SubscribeDataTrackErrorCode.ValueType # 0 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_INVALID_HANDLE: SubscribeDataTrackErrorCode.ValueType # 1 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_UNPUBLISHED: SubscribeDataTrackErrorCode.ValueType # 2 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_TIMEOUT: SubscribeDataTrackErrorCode.ValueType # 3 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_DISCONNECTED: SubscribeDataTrackErrorCode.ValueType # 4 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_PROTOCOL_ERROR: SubscribeDataTrackErrorCode.ValueType # 5 +SUBSCRIBE_DATA_TRACK_ERROR_CODE_INTERNAL: SubscribeDataTrackErrorCode.ValueType # 6 +global___SubscribeDataTrackErrorCode = SubscribeDataTrackErrorCode + +@typing.final +class DataTrackInfo(google.protobuf.message.Message): + """MARK: - Common + + Information about a published data track. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + SID_FIELD_NUMBER: builtins.int + USES_E2EE_FIELD_NUMBER: builtins.int + SCHEMA_FIELD_NUMBER: builtins.int + FRAME_ENCODING_FIELD_NUMBER: builtins.int + name: builtins.str + """Name of the track assigned by the publisher.""" + sid: builtins.str + """SFU-assigned track identifier.""" + uses_e2ee: builtins.bool + """Whether or not frames sent on the track use end-to-end encryption.""" + @property + def schema(self) -> global___DataTrackSchemaId: + """Schema associated with frames sent on the track, if any.""" + + @property + def frame_encoding(self) -> global___DataTrackFrameEncoding: + """Encoding of frames sent on the track, if specified.""" + + def __init__( + self, + *, + name: builtins.str | None = ..., + sid: builtins.str | None = ..., + uses_e2ee: builtins.bool | None = ..., + schema: global___DataTrackSchemaId | None = ..., + frame_encoding: global___DataTrackFrameEncoding | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["frame_encoding", b"frame_encoding", "name", b"name", "schema", b"schema", "sid", b"sid", "uses_e2ee", b"uses_e2ee"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame_encoding", b"frame_encoding", "name", b"name", "schema", b"schema", "sid", b"sid", "uses_e2ee", b"uses_e2ee"]) -> None: ... + +global___DataTrackInfo = DataTrackInfo + +@typing.final +class DataTrackFrame(google.protobuf.message.Message): + """A frame published on a data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PAYLOAD_FIELD_NUMBER: builtins.int + USER_TIMESTAMP_FIELD_NUMBER: builtins.int + payload: builtins.bytes + user_timestamp: builtins.int + def __init__( + self, + *, + payload: builtins.bytes | None = ..., + user_timestamp: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["payload", b"payload", "user_timestamp", b"user_timestamp"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["payload", b"payload", "user_timestamp", b"user_timestamp"]) -> None: ... + +global___DataTrackFrame = DataTrackFrame + +@typing.final +class DataTrackError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CODE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + code: global___DataTrackErrorCode.ValueType + message: builtins.str + def __init__( + self, + *, + code: global___DataTrackErrorCode.ValueType | None = ..., + message: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> None: ... + +global___DataTrackError = DataTrackError + +@typing.final +class PublishDataTrackError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CODE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + code: global___PublishDataTrackErrorCode.ValueType + message: builtins.str + def __init__( + self, + *, + code: global___PublishDataTrackErrorCode.ValueType | None = ..., + message: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> None: ... + +global___PublishDataTrackError = PublishDataTrackError + +@typing.final +class LocalDataTrackTryPushError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CODE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + code: global___LocalDataTrackTryPushErrorCode.ValueType + message: builtins.str + def __init__( + self, + *, + code: global___LocalDataTrackTryPushErrorCode.ValueType | None = ..., + message: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> None: ... + +global___LocalDataTrackTryPushError = LocalDataTrackTryPushError + +@typing.final +class SubscribeDataTrackError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CODE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + code: global___SubscribeDataTrackErrorCode.ValueType + message: builtins.str + def __init__( + self, + *, + code: global___SubscribeDataTrackErrorCode.ValueType | None = ..., + message: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "message", b"message"]) -> None: ... + +global___SubscribeDataTrackError = SubscribeDataTrackError + +@typing.final +class DataTrackOptions(google.protobuf.message.Message): + """MARK: - Local + + Options for publishing a data track. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + SCHEMA_FIELD_NUMBER: builtins.int + FRAME_ENCODING_FIELD_NUMBER: builtins.int + name: builtins.str + """Track name used to identify the track to other participants. + + Must not be empty and must be unique per publisher. + """ + @property + def schema(self) -> global___DataTrackSchemaId: + """Schema describing frames sent on the track.""" + + @property + def frame_encoding(self) -> global___DataTrackFrameEncoding: + """Encoding of frames sent on the track.""" + + def __init__( + self, + *, + name: builtins.str | None = ..., + schema: global___DataTrackSchemaId | None = ..., + frame_encoding: global___DataTrackFrameEncoding | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["frame_encoding", b"frame_encoding", "name", b"name", "schema", b"schema"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame_encoding", b"frame_encoding", "name", b"name", "schema", b"schema"]) -> None: ... + +global___DataTrackOptions = DataTrackOptions + +@typing.final +class PublishDataTrackRequest(google.protobuf.message.Message): + """Publish a data track""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + request_async_id: builtins.int + @property + def options(self) -> global___DataTrackOptions: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + options: global___DataTrackOptions | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id"]) -> None: ... + +global___PublishDataTrackRequest = PublishDataTrackRequest + +@typing.final +class PublishDataTrackResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___PublishDataTrackResponse = PublishDataTrackResponse + +@typing.final +class PublishDataTrackCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + TRACK_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + @property + def track(self) -> global___OwnedLocalDataTrack: ... + @property + def error(self) -> global___PublishDataTrackError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + track: global___OwnedLocalDataTrack | None = ..., + error: global___PublishDataTrackError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "track", b"track"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "result", b"result", "track", b"track"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["result", b"result"]) -> typing.Literal["track", "error"] | None: ... + +global___PublishDataTrackCallback = PublishDataTrackCallback + +@typing.final +class OwnedLocalDataTrack(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___DataTrackInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___DataTrackInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... + +global___OwnedLocalDataTrack = OwnedLocalDataTrack + +@typing.final +class LocalDataTrackTryPushRequest(google.protobuf.message.Message): + """Try pushing a frame to subscribers of the track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + FRAME_FIELD_NUMBER: builtins.int + track_handle: builtins.int + @property + def frame(self) -> global___DataTrackFrame: ... + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + frame: global___DataTrackFrame | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["frame", b"frame", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame", b"frame", "track_handle", b"track_handle"]) -> None: ... + +global___LocalDataTrackTryPushRequest = LocalDataTrackTryPushRequest + +@typing.final +class LocalDataTrackTryPushResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + @property + def error(self) -> global___LocalDataTrackTryPushError: ... + def __init__( + self, + *, + error: global___LocalDataTrackTryPushError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___LocalDataTrackTryPushResponse = LocalDataTrackTryPushResponse + +@typing.final +class LocalDataTrackIsPublishedRequest(google.protobuf.message.Message): + """Checks if the track is still published.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + track_handle: builtins.int + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> None: ... + +global___LocalDataTrackIsPublishedRequest = LocalDataTrackIsPublishedRequest + +@typing.final +class LocalDataTrackIsPublishedResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IS_PUBLISHED_FIELD_NUMBER: builtins.int + is_published: builtins.bool + def __init__( + self, + *, + is_published: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["is_published", b"is_published"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["is_published", b"is_published"]) -> None: ... + +global___LocalDataTrackIsPublishedResponse = LocalDataTrackIsPublishedResponse + +@typing.final +class LocalDataTrackUnpublishRequest(google.protobuf.message.Message): + """Unpublishes the track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + track_handle: builtins.int + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> None: ... + +global___LocalDataTrackUnpublishRequest = LocalDataTrackUnpublishRequest + +@typing.final +class LocalDataTrackUnpublishResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___LocalDataTrackUnpublishResponse = LocalDataTrackUnpublishResponse + +@typing.final +class OwnedRemoteDataTrack(google.protobuf.message.Message): + """MARK: - Remote""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + PUBLISHER_IDENTITY_FIELD_NUMBER: builtins.int + publisher_identity: builtins.str + """Identity of the remote participant who published the track.""" + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + @property + def info(self) -> global___DataTrackInfo: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + info: global___DataTrackInfo | None = ..., + publisher_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info", "publisher_identity", b"publisher_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info", "publisher_identity", b"publisher_identity"]) -> None: ... + +global___OwnedRemoteDataTrack = OwnedRemoteDataTrack + +@typing.final +class OwnedDataTrackStream(google.protobuf.message.Message): + """Handle to an active data track subscription. + + Dropping the handle will unsubscribe from the track. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + HANDLE_FIELD_NUMBER: builtins.int + @property + def handle(self) -> handle_pb2.FfiOwnedHandle: ... + def __init__( + self, + *, + handle: handle_pb2.FfiOwnedHandle | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle"]) -> None: ... + +global___OwnedDataTrackStream = OwnedDataTrackStream + +@typing.final +class DataTrackSubscribeOptions(google.protobuf.message.Message): + """Reserved for future subscription options.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + BUFFER_SIZE_FIELD_NUMBER: builtins.int + buffer_size: builtins.int + """Maximum number of frames to buffer internally.""" + def __init__( + self, + *, + buffer_size: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["buffer_size", b"buffer_size"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer_size", b"buffer_size"]) -> None: ... + +global___DataTrackSubscribeOptions = DataTrackSubscribeOptions + +@typing.final +class RemoteDataTrackIsPublishedRequest(google.protobuf.message.Message): + """Checks if the track is still published.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + track_handle: builtins.int + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track_handle", b"track_handle"]) -> None: ... + +global___RemoteDataTrackIsPublishedRequest = RemoteDataTrackIsPublishedRequest + +@typing.final +class RemoteDataTrackIsPublishedResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + IS_PUBLISHED_FIELD_NUMBER: builtins.int + is_published: builtins.bool + def __init__( + self, + *, + is_published: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["is_published", b"is_published"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["is_published", b"is_published"]) -> None: ... + +global___RemoteDataTrackIsPublishedResponse = RemoteDataTrackIsPublishedResponse + +@typing.final +class RemoteDataTrackPipelineOptions(google.protobuf.message.Message): + """Track-level options that configure how the incoming-frame pipeline reassembles packets for a remote data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MAX_PARTIAL_FRAMES_FIELD_NUMBER: builtins.int + max_partial_frames: builtins.int + """Maximum number of partial frames the depacketizer will track concurrently for this track. + + Zero is not a valid value; if a value of zero is provided, it will be clamped to one. + """ + def __init__( + self, + *, + max_partial_frames: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["max_partial_frames", b"max_partial_frames"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["max_partial_frames", b"max_partial_frames"]) -> None: ... + +global___RemoteDataTrackPipelineOptions = RemoteDataTrackPipelineOptions + +@typing.final +class RemoteDataTrackSetPipelineOptionsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + track_handle: builtins.int + @property + def options(self) -> global___RemoteDataTrackPipelineOptions: ... + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + options: global___RemoteDataTrackPipelineOptions | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["options", b"options", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["options", b"options", "track_handle", b"track_handle"]) -> None: ... + +global___RemoteDataTrackSetPipelineOptionsRequest = RemoteDataTrackSetPipelineOptionsRequest + +@typing.final +class RemoteDataTrackSetPipelineOptionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___RemoteDataTrackSetPipelineOptionsResponse = RemoteDataTrackSetPipelineOptionsResponse + +@typing.final +class SubscribeDataTrackRequest(google.protobuf.message.Message): + """Subscribe to a data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + OPTIONS_FIELD_NUMBER: builtins.int + track_handle: builtins.int + @property + def options(self) -> global___DataTrackSubscribeOptions: ... + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + options: global___DataTrackSubscribeOptions | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["options", b"options", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["options", b"options", "track_handle", b"track_handle"]) -> None: ... + +global___SubscribeDataTrackRequest = SubscribeDataTrackRequest + +@typing.final +class SubscribeDataTrackResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_FIELD_NUMBER: builtins.int + @property + def stream(self) -> global___OwnedDataTrackStream: ... + def __init__( + self, + *, + stream: global___OwnedDataTrackStream | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream", b"stream"]) -> None: ... + +global___SubscribeDataTrackResponse = SubscribeDataTrackResponse + +@typing.final +class DataTrackStreamReadRequest(google.protobuf.message.Message): + """Signal readiness to handle the next frame. + + This allows the client to put backpressure on the internal receive buffer. + Sending this request will cause the next frame to be sent via `DataTrackStreamFrameReceived` + once one is available. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_HANDLE_FIELD_NUMBER: builtins.int + stream_handle: builtins.int + def __init__( + self, + *, + stream_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["stream_handle", b"stream_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream_handle", b"stream_handle"]) -> None: ... + +global___DataTrackStreamReadRequest = DataTrackStreamReadRequest + +@typing.final +class DataTrackStreamReadResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + EOS_EVENT_FIELD_NUMBER: builtins.int + @property + def eos_event(self) -> global___DataTrackStreamEOS: + """The end-of-stream event if the stream has already ended.""" + + def __init__( + self, + *, + eos_event: global___DataTrackStreamEOS | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["eos_event", b"eos_event"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["eos_event", b"eos_event"]) -> None: ... + +global___DataTrackStreamReadResponse = DataTrackStreamReadResponse + +@typing.final +class DataTrackStreamEvent(google.protobuf.message.Message): + """Event emitted on an active stream.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_HANDLE_FIELD_NUMBER: builtins.int + FRAME_RECEIVED_FIELD_NUMBER: builtins.int + EOS_FIELD_NUMBER: builtins.int + stream_handle: builtins.int + @property + def frame_received(self) -> global___DataTrackStreamFrameReceived: ... + @property + def eos(self) -> global___DataTrackStreamEOS: ... + def __init__( + self, + *, + stream_handle: builtins.int | None = ..., + frame_received: global___DataTrackStreamFrameReceived | None = ..., + eos: global___DataTrackStreamEOS | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["detail", b"detail", "eos", b"eos", "frame_received", b"frame_received", "stream_handle", b"stream_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["detail", b"detail", "eos", b"eos", "frame_received", b"frame_received", "stream_handle", b"stream_handle"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["detail", b"detail"]) -> typing.Literal["frame_received", "eos"] | None: ... + +global___DataTrackStreamEvent = DataTrackStreamEvent + +@typing.final +class DataTrackStreamFrameReceived(google.protobuf.message.Message): + """A frame was received.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + FRAME_FIELD_NUMBER: builtins.int + @property + def frame(self) -> global___DataTrackFrame: ... + def __init__( + self, + *, + frame: global___DataTrackFrame | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["frame", b"frame"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame", b"frame"]) -> None: ... + +global___DataTrackStreamFrameReceived = DataTrackStreamFrameReceived + +@typing.final +class DataTrackStreamEOS(google.protobuf.message.Message): + """Stream has ended.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + @property + def error(self) -> global___SubscribeDataTrackError: + """Present if stream ended before any frames were emitted due to subscription establishment failing. + Absent if the stream ended normally (i.e., due to the track being unpublished). + """ + + def __init__( + self, + *, + error: global___SubscribeDataTrackError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___DataTrackStreamEOS = DataTrackStreamEOS + +@typing.final +class DataTrackSchemaEncoding(google.protobuf.message.Message): + """MARK: - Schemas + + Encoding used to interpret a data track schema definition. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _WellKnownSchemaEncoding: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _WellKnownSchemaEncodingEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 0 + WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 1 + WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 2 + WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 3 + WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 4 + WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 5 + WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 6 + WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA: DataTrackSchemaEncoding._WellKnownSchemaEncoding.ValueType # 7 + + class WellKnownSchemaEncoding(_WellKnownSchemaEncoding, metaclass=_WellKnownSchemaEncodingEnumTypeWrapper): + """Well-known encoding for a schema definition.""" + + WELL_KNOWN_SCHEMA_ENCODING_UNSPECIFIED: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 0 + WELL_KNOWN_SCHEMA_ENCODING_PROTOBUF: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 1 + WELL_KNOWN_SCHEMA_ENCODING_FLATBUFFER: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 2 + WELL_KNOWN_SCHEMA_ENCODING_ROS1_MSG: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 3 + WELL_KNOWN_SCHEMA_ENCODING_ROS2_MSG: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 4 + WELL_KNOWN_SCHEMA_ENCODING_ROS2_IDL: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 5 + WELL_KNOWN_SCHEMA_ENCODING_OMG_IDL: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 6 + WELL_KNOWN_SCHEMA_ENCODING_JSON_SCHEMA: DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType # 7 + + WELL_KNOWN_FIELD_NUMBER: builtins.int + CUSTOM_FIELD_NUMBER: builtins.int + well_known: global___DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType + custom: builtins.str + """Identifier of a custom encoding not covered by the well-known cases. + This must be non-empty and no longer than 25 characters. + """ + def __init__( + self, + *, + well_known: global___DataTrackSchemaEncoding.WellKnownSchemaEncoding.ValueType | None = ..., + custom: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["custom", b"custom", "encoding", b"encoding", "well_known", b"well_known"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["custom", b"custom", "encoding", b"encoding", "well_known", b"well_known"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["encoding", b"encoding"]) -> typing.Literal["well_known", "custom"] | None: ... + +global___DataTrackSchemaEncoding = DataTrackSchemaEncoding + +@typing.final +class DataTrackFrameEncoding(google.protobuf.message.Message): + """Encoding used for frames sent on a data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _WellKnownFrameEncoding: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _WellKnownFrameEncodingEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 0 + WELL_KNOWN_FRAME_ENCODING_ROS1: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 1 + WELL_KNOWN_FRAME_ENCODING_CDR: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 2 + WELL_KNOWN_FRAME_ENCODING_PROTOBUF: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 3 + WELL_KNOWN_FRAME_ENCODING_FLATBUFFER: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 4 + WELL_KNOWN_FRAME_ENCODING_CBOR: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 5 + WELL_KNOWN_FRAME_ENCODING_MSGPACK: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 6 + WELL_KNOWN_FRAME_ENCODING_JSON: DataTrackFrameEncoding._WellKnownFrameEncoding.ValueType # 7 + + class WellKnownFrameEncoding(_WellKnownFrameEncoding, metaclass=_WellKnownFrameEncodingEnumTypeWrapper): + """Well-known encoding for frame payloads.""" + + WELL_KNOWN_FRAME_ENCODING_UNSPECIFIED: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 0 + WELL_KNOWN_FRAME_ENCODING_ROS1: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 1 + WELL_KNOWN_FRAME_ENCODING_CDR: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 2 + WELL_KNOWN_FRAME_ENCODING_PROTOBUF: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 3 + WELL_KNOWN_FRAME_ENCODING_FLATBUFFER: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 4 + WELL_KNOWN_FRAME_ENCODING_CBOR: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 5 + WELL_KNOWN_FRAME_ENCODING_MSGPACK: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 6 + WELL_KNOWN_FRAME_ENCODING_JSON: DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType # 7 + + WELL_KNOWN_FIELD_NUMBER: builtins.int + CUSTOM_FIELD_NUMBER: builtins.int + well_known: global___DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType + custom: builtins.str + """Identifier of a custom encoding not covered by the well-known cases. + This must be non-empty and no longer than 25 characters. + """ + def __init__( + self, + *, + well_known: global___DataTrackFrameEncoding.WellKnownFrameEncoding.ValueType | None = ..., + custom: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["custom", b"custom", "encoding", b"encoding", "well_known", b"well_known"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["custom", b"custom", "encoding", b"encoding", "well_known", b"well_known"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["encoding", b"encoding"]) -> typing.Literal["well_known", "custom"] | None: ... + +global___DataTrackFrameEncoding = DataTrackFrameEncoding + +@typing.final +class DataTrackSchemaId(google.protobuf.message.Message): + """Uniquely identifies a data track schema.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + ENCODING_FIELD_NUMBER: builtins.int + name: builtins.str + """Name component of the schema identifier.""" + @property + def encoding(self) -> global___DataTrackSchemaEncoding: + """Encoding of the schema definition.""" + + def __init__( + self, + *, + name: builtins.str | None = ..., + encoding: global___DataTrackSchemaEncoding | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["encoding", b"encoding", "name", b"name"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["encoding", b"encoding", "name", b"name"]) -> None: ... + +global___DataTrackSchemaId = DataTrackSchemaId + +@typing.final +class DefineSchemaRequest(google.protobuf.message.Message): + """Define (store) a schema definition for the local participant.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + SCHEMA_ID_FIELD_NUMBER: builtins.int + DEFINITION_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + definition: builtins.str + request_async_id: builtins.int + @property + def schema_id(self) -> global___DataTrackSchemaId: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + schema_id: global___DataTrackSchemaId | None = ..., + definition: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["definition", b"definition", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "schema_id", b"schema_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["definition", b"definition", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "schema_id", b"schema_id"]) -> None: ... + +global___DefineSchemaRequest = DefineSchemaRequest + +@typing.final +class DefineSchemaResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___DefineSchemaResponse = DefineSchemaResponse + +@typing.final +class DefineSchemaCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + """Present if the schema could not be defined.""" + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___DefineSchemaCallback = DefineSchemaCallback + +@typing.final +class GetSchemaRequest(google.protobuf.message.Message): + """Retrieve a schema definition previously stored by a participant.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + SCHEMA_ID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + participant_identity: builtins.str + """Identity of the participant who owns the schema.""" + request_async_id: builtins.int + @property + def schema_id(self) -> global___DataTrackSchemaId: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + schema_id: global___DataTrackSchemaId | None = ..., + participant_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "participant_identity", b"participant_identity", "request_async_id", b"request_async_id", "schema_id", b"schema_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "participant_identity", b"participant_identity", "request_async_id", b"request_async_id", "schema_id", b"schema_id"]) -> None: ... + +global___GetSchemaRequest = GetSchemaRequest + +@typing.final +class GetSchemaResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___GetSchemaResponse = GetSchemaResponse + +@typing.final +class GetSchemaCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + DEFINITION_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + definition: builtins.str + """The schema definition, present on success.""" + error: builtins.str + """Present if the schema could not be retrieved.""" + def __init__( + self, + *, + async_id: builtins.int | None = ..., + definition: builtins.str | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "definition", b"definition", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "definition", b"definition", "error", b"error"]) -> None: ... + +global___GetSchemaCallback = GetSchemaCallback diff --git a/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.py b/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.py index 9794751d..0a0b9a52 100644 --- a/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: e2ee.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ne2ee.proto\x12\rlivekit.proto\"c\n\x0c\x46rameCryptor\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x11\n\tkey_index\x18\x03 \x01(\x05\x12\x0f\n\x07\x65nabled\x18\x04 \x01(\x08\"\x8a\x01\n\x12KeyProviderOptions\x12\x17\n\nshared_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x12\x1b\n\x13ratchet_window_size\x18\x02 \x01(\x05\x12\x14\n\x0cratchet_salt\x18\x03 \x01(\x0c\x12\x19\n\x11\x66\x61ilure_tolerance\x18\x04 \x01(\x05\x42\r\n\x0b_shared_key\"\x86\x01\n\x0b\x45\x32\x65\x65Options\x12\x36\n\x0f\x65ncryption_type\x18\x01 \x01(\x0e\x32\x1d.livekit.proto.EncryptionType\x12?\n\x14key_provider_options\x18\x02 \x01(\x0b\x32!.livekit.proto.KeyProviderOptions\"/\n\x1c\x45\x32\x65\x65ManagerSetEnabledRequest\x12\x0f\n\x07\x65nabled\x18\x01 \x01(\x08\"\x1f\n\x1d\x45\x32\x65\x65ManagerSetEnabledResponse\"$\n\"E2eeManagerGetFrameCryptorsRequest\"Z\n#E2eeManagerGetFrameCryptorsResponse\x12\x33\n\x0e\x66rame_cryptors\x18\x01 \x03(\x0b\x32\x1b.livekit.proto.FrameCryptor\"a\n\x1d\x46rameCryptorSetEnabledRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x0f\n\x07\x65nabled\x18\x03 \x01(\x08\" \n\x1e\x46rameCryptorSetEnabledResponse\"d\n\x1e\x46rameCryptorSetKeyIndexRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x11\n\tkey_index\x18\x03 \x01(\x05\"!\n\x1f\x46rameCryptorSetKeyIndexResponse\"<\n\x13SetSharedKeyRequest\x12\x12\n\nshared_key\x18\x01 \x01(\x0c\x12\x11\n\tkey_index\x18\x02 \x01(\x05\"\x16\n\x14SetSharedKeyResponse\",\n\x17RatchetSharedKeyRequest\x12\x11\n\tkey_index\x18\x01 \x01(\x05\"<\n\x18RatchetSharedKeyResponse\x12\x14\n\x07new_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_new_key\"(\n\x13GetSharedKeyRequest\x12\x11\n\tkey_index\x18\x01 \x01(\x05\"0\n\x14GetSharedKeyResponse\x12\x10\n\x03key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x06\n\x04_key\"M\n\rSetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x0b\n\x03key\x18\x02 \x01(\x0c\x12\x11\n\tkey_index\x18\x03 \x01(\x05\"\x10\n\x0eSetKeyResponse\"D\n\x11RatchetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\tkey_index\x18\x02 \x01(\x05\"6\n\x12RatchetKeyResponse\x12\x14\n\x07new_key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\n\n\x08_new_key\"@\n\rGetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\tkey_index\x18\x02 \x01(\x05\"*\n\x0eGetKeyResponse\x12\x10\n\x03key\x18\x01 \x01(\x0cH\x00\x88\x01\x01\x42\x06\n\x04_key\"\xcc\x05\n\x0b\x45\x32\x65\x65Request\x12\x13\n\x0broom_handle\x18\x01 \x01(\x04\x12J\n\x13manager_set_enabled\x18\x02 \x01(\x0b\x32+.livekit.proto.E2eeManagerSetEnabledRequestH\x00\x12W\n\x1amanager_get_frame_cryptors\x18\x03 \x01(\x0b\x32\x31.livekit.proto.E2eeManagerGetFrameCryptorsRequestH\x00\x12K\n\x13\x63ryptor_set_enabled\x18\x04 \x01(\x0b\x32,.livekit.proto.FrameCryptorSetEnabledRequestH\x00\x12N\n\x15\x63ryptor_set_key_index\x18\x05 \x01(\x0b\x32-.livekit.proto.FrameCryptorSetKeyIndexRequestH\x00\x12<\n\x0eset_shared_key\x18\x06 \x01(\x0b\x32\".livekit.proto.SetSharedKeyRequestH\x00\x12\x44\n\x12ratchet_shared_key\x18\x07 \x01(\x0b\x32&.livekit.proto.RatchetSharedKeyRequestH\x00\x12<\n\x0eget_shared_key\x18\x08 \x01(\x0b\x32\".livekit.proto.GetSharedKeyRequestH\x00\x12/\n\x07set_key\x18\t \x01(\x0b\x32\x1c.livekit.proto.SetKeyRequestH\x00\x12\x37\n\x0bratchet_key\x18\n \x01(\x0b\x32 .livekit.proto.RatchetKeyRequestH\x00\x12/\n\x07get_key\x18\x0b \x01(\x0b\x32\x1c.livekit.proto.GetKeyRequestH\x00\x42\t\n\x07message\"\xc2\x05\n\x0c\x45\x32\x65\x65Response\x12K\n\x13manager_set_enabled\x18\x01 \x01(\x0b\x32,.livekit.proto.E2eeManagerSetEnabledResponseH\x00\x12X\n\x1amanager_get_frame_cryptors\x18\x02 \x01(\x0b\x32\x32.livekit.proto.E2eeManagerGetFrameCryptorsResponseH\x00\x12L\n\x13\x63ryptor_set_enabled\x18\x03 \x01(\x0b\x32-.livekit.proto.FrameCryptorSetEnabledResponseH\x00\x12O\n\x15\x63ryptor_set_key_index\x18\x04 \x01(\x0b\x32..livekit.proto.FrameCryptorSetKeyIndexResponseH\x00\x12=\n\x0eset_shared_key\x18\x05 \x01(\x0b\x32#.livekit.proto.SetSharedKeyResponseH\x00\x12\x45\n\x12ratchet_shared_key\x18\x06 \x01(\x0b\x32\'.livekit.proto.RatchetSharedKeyResponseH\x00\x12=\n\x0eget_shared_key\x18\x07 \x01(\x0b\x32#.livekit.proto.GetSharedKeyResponseH\x00\x12\x30\n\x07set_key\x18\x08 \x01(\x0b\x32\x1d.livekit.proto.SetKeyResponseH\x00\x12\x38\n\x0bratchet_key\x18\t \x01(\x0b\x32!.livekit.proto.RatchetKeyResponseH\x00\x12\x30\n\x07get_key\x18\n \x01(\x0b\x32\x1d.livekit.proto.GetKeyResponseH\x00\x42\t\n\x07message*/\n\x0e\x45ncryptionType\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03GCM\x10\x01\x12\n\n\x06\x43USTOM\x10\x02*\x88\x01\n\x0f\x45ncryptionState\x12\x07\n\x03NEW\x10\x00\x12\x06\n\x02OK\x10\x01\x12\x15\n\x11\x45NCRYPTION_FAILED\x10\x02\x12\x15\n\x11\x44\x45\x43RYPTION_FAILED\x10\x03\x12\x0f\n\x0bMISSING_KEY\x10\x04\x12\x11\n\rKEY_RATCHETED\x10\x05\x12\x12\n\x0eINTERNAL_ERROR\x10\x06\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\ne2ee.proto\x12\rlivekit.proto\"c\n\x0c\x46rameCryptor\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\x12\x11\n\tkey_index\x18\x03 \x02(\x05\x12\x0f\n\x07\x65nabled\x18\x04 \x02(\x08\"\xd4\x01\n\x12KeyProviderOptions\x12\x12\n\nshared_key\x18\x01 \x01(\x0c\x12\x1b\n\x13ratchet_window_size\x18\x02 \x02(\x05\x12\x14\n\x0cratchet_salt\x18\x03 \x02(\x0c\x12\x19\n\x11\x66\x61ilure_tolerance\x18\x04 \x02(\x05\x12\x15\n\rkey_ring_size\x18\x05 \x02(\x05\x12\x45\n\x17key_derivation_function\x18\x06 \x02(\x0e\x32$.livekit.proto.KeyDerivationFunction\"\x86\x01\n\x0b\x45\x32\x65\x65Options\x12\x36\n\x0f\x65ncryption_type\x18\x01 \x02(\x0e\x32\x1d.livekit.proto.EncryptionType\x12?\n\x14key_provider_options\x18\x02 \x02(\x0b\x32!.livekit.proto.KeyProviderOptions\"/\n\x1c\x45\x32\x65\x65ManagerSetEnabledRequest\x12\x0f\n\x07\x65nabled\x18\x01 \x02(\x08\"\x1f\n\x1d\x45\x32\x65\x65ManagerSetEnabledResponse\"$\n\"E2eeManagerGetFrameCryptorsRequest\"Z\n#E2eeManagerGetFrameCryptorsResponse\x12\x33\n\x0e\x66rame_cryptors\x18\x01 \x03(\x0b\x32\x1b.livekit.proto.FrameCryptor\"a\n\x1d\x46rameCryptorSetEnabledRequest\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\x12\x0f\n\x07\x65nabled\x18\x03 \x02(\x08\" \n\x1e\x46rameCryptorSetEnabledResponse\"d\n\x1e\x46rameCryptorSetKeyIndexRequest\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\x12\x11\n\tkey_index\x18\x03 \x02(\x05\"!\n\x1f\x46rameCryptorSetKeyIndexResponse\"<\n\x13SetSharedKeyRequest\x12\x12\n\nshared_key\x18\x01 \x02(\x0c\x12\x11\n\tkey_index\x18\x02 \x02(\x05\"\x16\n\x14SetSharedKeyResponse\",\n\x17RatchetSharedKeyRequest\x12\x11\n\tkey_index\x18\x01 \x02(\x05\"+\n\x18RatchetSharedKeyResponse\x12\x0f\n\x07new_key\x18\x01 \x01(\x0c\"(\n\x13GetSharedKeyRequest\x12\x11\n\tkey_index\x18\x01 \x02(\x05\"#\n\x14GetSharedKeyResponse\x12\x0b\n\x03key\x18\x01 \x01(\x0c\"M\n\rSetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x0b\n\x03key\x18\x02 \x02(\x0c\x12\x11\n\tkey_index\x18\x03 \x02(\x05\"\x10\n\x0eSetKeyResponse\"D\n\x11RatchetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\tkey_index\x18\x02 \x02(\x05\"%\n\x12RatchetKeyResponse\x12\x0f\n\x07new_key\x18\x01 \x01(\x0c\"@\n\rGetKeyRequest\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\tkey_index\x18\x02 \x02(\x05\"\x1d\n\x0eGetKeyResponse\x12\x0b\n\x03key\x18\x01 \x01(\x0c\"\xcc\x05\n\x0b\x45\x32\x65\x65Request\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\x12J\n\x13manager_set_enabled\x18\x02 \x01(\x0b\x32+.livekit.proto.E2eeManagerSetEnabledRequestH\x00\x12W\n\x1amanager_get_frame_cryptors\x18\x03 \x01(\x0b\x32\x31.livekit.proto.E2eeManagerGetFrameCryptorsRequestH\x00\x12K\n\x13\x63ryptor_set_enabled\x18\x04 \x01(\x0b\x32,.livekit.proto.FrameCryptorSetEnabledRequestH\x00\x12N\n\x15\x63ryptor_set_key_index\x18\x05 \x01(\x0b\x32-.livekit.proto.FrameCryptorSetKeyIndexRequestH\x00\x12<\n\x0eset_shared_key\x18\x06 \x01(\x0b\x32\".livekit.proto.SetSharedKeyRequestH\x00\x12\x44\n\x12ratchet_shared_key\x18\x07 \x01(\x0b\x32&.livekit.proto.RatchetSharedKeyRequestH\x00\x12<\n\x0eget_shared_key\x18\x08 \x01(\x0b\x32\".livekit.proto.GetSharedKeyRequestH\x00\x12/\n\x07set_key\x18\t \x01(\x0b\x32\x1c.livekit.proto.SetKeyRequestH\x00\x12\x37\n\x0bratchet_key\x18\n \x01(\x0b\x32 .livekit.proto.RatchetKeyRequestH\x00\x12/\n\x07get_key\x18\x0b \x01(\x0b\x32\x1c.livekit.proto.GetKeyRequestH\x00\x42\t\n\x07message\"\xc2\x05\n\x0c\x45\x32\x65\x65Response\x12K\n\x13manager_set_enabled\x18\x01 \x01(\x0b\x32,.livekit.proto.E2eeManagerSetEnabledResponseH\x00\x12X\n\x1amanager_get_frame_cryptors\x18\x02 \x01(\x0b\x32\x32.livekit.proto.E2eeManagerGetFrameCryptorsResponseH\x00\x12L\n\x13\x63ryptor_set_enabled\x18\x03 \x01(\x0b\x32-.livekit.proto.FrameCryptorSetEnabledResponseH\x00\x12O\n\x15\x63ryptor_set_key_index\x18\x04 \x01(\x0b\x32..livekit.proto.FrameCryptorSetKeyIndexResponseH\x00\x12=\n\x0eset_shared_key\x18\x05 \x01(\x0b\x32#.livekit.proto.SetSharedKeyResponseH\x00\x12\x45\n\x12ratchet_shared_key\x18\x06 \x01(\x0b\x32\'.livekit.proto.RatchetSharedKeyResponseH\x00\x12=\n\x0eget_shared_key\x18\x07 \x01(\x0b\x32#.livekit.proto.GetSharedKeyResponseH\x00\x12\x30\n\x07set_key\x18\x08 \x01(\x0b\x32\x1d.livekit.proto.SetKeyResponseH\x00\x12\x38\n\x0bratchet_key\x18\t \x01(\x0b\x32!.livekit.proto.RatchetKeyResponseH\x00\x12\x30\n\x07get_key\x18\n \x01(\x0b\x32\x1d.livekit.proto.GetKeyResponseH\x00\x42\t\n\x07message*/\n\x0e\x45ncryptionType\x12\x08\n\x04NONE\x10\x00\x12\x07\n\x03GCM\x10\x01\x12\n\n\x06\x43USTOM\x10\x02*-\n\x15KeyDerivationFunction\x12\n\n\x06PBKDF2\x10\x00\x12\x08\n\x04HKDF\x10\x01*\x88\x01\n\x0f\x45ncryptionState\x12\x07\n\x03NEW\x10\x00\x12\x06\n\x02OK\x10\x01\x12\x15\n\x11\x45NCRYPTION_FAILED\x10\x02\x12\x15\n\x11\x44\x45\x43RYPTION_FAILED\x10\x03\x12\x0f\n\x0bMISSING_KEY\x10\x04\x12\x11\n\rKEY_RATCHETED\x10\x05\x12\x12\n\x0eINTERNAL_ERROR\x10\x06\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -22,58 +22,60 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_ENCRYPTIONTYPE']._serialized_start=2937 - _globals['_ENCRYPTIONTYPE']._serialized_end=2984 - _globals['_ENCRYPTIONSTATE']._serialized_start=2987 - _globals['_ENCRYPTIONSTATE']._serialized_end=3123 + _globals['_ENCRYPTIONTYPE']._serialized_start=2951 + _globals['_ENCRYPTIONTYPE']._serialized_end=2998 + _globals['_KEYDERIVATIONFUNCTION']._serialized_start=3000 + _globals['_KEYDERIVATIONFUNCTION']._serialized_end=3045 + _globals['_ENCRYPTIONSTATE']._serialized_start=3048 + _globals['_ENCRYPTIONSTATE']._serialized_end=3184 _globals['_FRAMECRYPTOR']._serialized_start=29 _globals['_FRAMECRYPTOR']._serialized_end=128 _globals['_KEYPROVIDEROPTIONS']._serialized_start=131 - _globals['_KEYPROVIDEROPTIONS']._serialized_end=269 - _globals['_E2EEOPTIONS']._serialized_start=272 - _globals['_E2EEOPTIONS']._serialized_end=406 - _globals['_E2EEMANAGERSETENABLEDREQUEST']._serialized_start=408 - _globals['_E2EEMANAGERSETENABLEDREQUEST']._serialized_end=455 - _globals['_E2EEMANAGERSETENABLEDRESPONSE']._serialized_start=457 - _globals['_E2EEMANAGERSETENABLEDRESPONSE']._serialized_end=488 - _globals['_E2EEMANAGERGETFRAMECRYPTORSREQUEST']._serialized_start=490 - _globals['_E2EEMANAGERGETFRAMECRYPTORSREQUEST']._serialized_end=526 - _globals['_E2EEMANAGERGETFRAMECRYPTORSRESPONSE']._serialized_start=528 - _globals['_E2EEMANAGERGETFRAMECRYPTORSRESPONSE']._serialized_end=618 - _globals['_FRAMECRYPTORSETENABLEDREQUEST']._serialized_start=620 - _globals['_FRAMECRYPTORSETENABLEDREQUEST']._serialized_end=717 - _globals['_FRAMECRYPTORSETENABLEDRESPONSE']._serialized_start=719 - _globals['_FRAMECRYPTORSETENABLEDRESPONSE']._serialized_end=751 - _globals['_FRAMECRYPTORSETKEYINDEXREQUEST']._serialized_start=753 - _globals['_FRAMECRYPTORSETKEYINDEXREQUEST']._serialized_end=853 - _globals['_FRAMECRYPTORSETKEYINDEXRESPONSE']._serialized_start=855 - _globals['_FRAMECRYPTORSETKEYINDEXRESPONSE']._serialized_end=888 - _globals['_SETSHAREDKEYREQUEST']._serialized_start=890 - _globals['_SETSHAREDKEYREQUEST']._serialized_end=950 - _globals['_SETSHAREDKEYRESPONSE']._serialized_start=952 - _globals['_SETSHAREDKEYRESPONSE']._serialized_end=974 - _globals['_RATCHETSHAREDKEYREQUEST']._serialized_start=976 - _globals['_RATCHETSHAREDKEYREQUEST']._serialized_end=1020 - _globals['_RATCHETSHAREDKEYRESPONSE']._serialized_start=1022 - _globals['_RATCHETSHAREDKEYRESPONSE']._serialized_end=1082 - _globals['_GETSHAREDKEYREQUEST']._serialized_start=1084 - _globals['_GETSHAREDKEYREQUEST']._serialized_end=1124 - _globals['_GETSHAREDKEYRESPONSE']._serialized_start=1126 - _globals['_GETSHAREDKEYRESPONSE']._serialized_end=1174 - _globals['_SETKEYREQUEST']._serialized_start=1176 - _globals['_SETKEYREQUEST']._serialized_end=1253 - _globals['_SETKEYRESPONSE']._serialized_start=1255 - _globals['_SETKEYRESPONSE']._serialized_end=1271 - _globals['_RATCHETKEYREQUEST']._serialized_start=1273 - _globals['_RATCHETKEYREQUEST']._serialized_end=1341 - _globals['_RATCHETKEYRESPONSE']._serialized_start=1343 - _globals['_RATCHETKEYRESPONSE']._serialized_end=1397 - _globals['_GETKEYREQUEST']._serialized_start=1399 - _globals['_GETKEYREQUEST']._serialized_end=1463 - _globals['_GETKEYRESPONSE']._serialized_start=1465 - _globals['_GETKEYRESPONSE']._serialized_end=1507 - _globals['_E2EEREQUEST']._serialized_start=1510 - _globals['_E2EEREQUEST']._serialized_end=2226 - _globals['_E2EERESPONSE']._serialized_start=2229 - _globals['_E2EERESPONSE']._serialized_end=2935 + _globals['_KEYPROVIDEROPTIONS']._serialized_end=343 + _globals['_E2EEOPTIONS']._serialized_start=346 + _globals['_E2EEOPTIONS']._serialized_end=480 + _globals['_E2EEMANAGERSETENABLEDREQUEST']._serialized_start=482 + _globals['_E2EEMANAGERSETENABLEDREQUEST']._serialized_end=529 + _globals['_E2EEMANAGERSETENABLEDRESPONSE']._serialized_start=531 + _globals['_E2EEMANAGERSETENABLEDRESPONSE']._serialized_end=562 + _globals['_E2EEMANAGERGETFRAMECRYPTORSREQUEST']._serialized_start=564 + _globals['_E2EEMANAGERGETFRAMECRYPTORSREQUEST']._serialized_end=600 + _globals['_E2EEMANAGERGETFRAMECRYPTORSRESPONSE']._serialized_start=602 + _globals['_E2EEMANAGERGETFRAMECRYPTORSRESPONSE']._serialized_end=692 + _globals['_FRAMECRYPTORSETENABLEDREQUEST']._serialized_start=694 + _globals['_FRAMECRYPTORSETENABLEDREQUEST']._serialized_end=791 + _globals['_FRAMECRYPTORSETENABLEDRESPONSE']._serialized_start=793 + _globals['_FRAMECRYPTORSETENABLEDRESPONSE']._serialized_end=825 + _globals['_FRAMECRYPTORSETKEYINDEXREQUEST']._serialized_start=827 + _globals['_FRAMECRYPTORSETKEYINDEXREQUEST']._serialized_end=927 + _globals['_FRAMECRYPTORSETKEYINDEXRESPONSE']._serialized_start=929 + _globals['_FRAMECRYPTORSETKEYINDEXRESPONSE']._serialized_end=962 + _globals['_SETSHAREDKEYREQUEST']._serialized_start=964 + _globals['_SETSHAREDKEYREQUEST']._serialized_end=1024 + _globals['_SETSHAREDKEYRESPONSE']._serialized_start=1026 + _globals['_SETSHAREDKEYRESPONSE']._serialized_end=1048 + _globals['_RATCHETSHAREDKEYREQUEST']._serialized_start=1050 + _globals['_RATCHETSHAREDKEYREQUEST']._serialized_end=1094 + _globals['_RATCHETSHAREDKEYRESPONSE']._serialized_start=1096 + _globals['_RATCHETSHAREDKEYRESPONSE']._serialized_end=1139 + _globals['_GETSHAREDKEYREQUEST']._serialized_start=1141 + _globals['_GETSHAREDKEYREQUEST']._serialized_end=1181 + _globals['_GETSHAREDKEYRESPONSE']._serialized_start=1183 + _globals['_GETSHAREDKEYRESPONSE']._serialized_end=1218 + _globals['_SETKEYREQUEST']._serialized_start=1220 + _globals['_SETKEYREQUEST']._serialized_end=1297 + _globals['_SETKEYRESPONSE']._serialized_start=1299 + _globals['_SETKEYRESPONSE']._serialized_end=1315 + _globals['_RATCHETKEYREQUEST']._serialized_start=1317 + _globals['_RATCHETKEYREQUEST']._serialized_end=1385 + _globals['_RATCHETKEYRESPONSE']._serialized_start=1387 + _globals['_RATCHETKEYRESPONSE']._serialized_end=1424 + _globals['_GETKEYREQUEST']._serialized_start=1426 + _globals['_GETKEYREQUEST']._serialized_end=1490 + _globals['_GETKEYRESPONSE']._serialized_start=1492 + _globals['_GETKEYRESPONSE']._serialized_end=1521 + _globals['_E2EEREQUEST']._serialized_start=1524 + _globals['_E2EEREQUEST']._serialized_end=2240 + _globals['_E2EERESPONSE']._serialized_start=2243 + _globals['_E2EERESPONSE']._serialized_end=2949 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.pyi index a34cf52d..53437b21 100644 --- a/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/e2ee_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import collections.abc import google.protobuf.descriptor @@ -49,6 +50,21 @@ GCM: EncryptionType.ValueType # 1 CUSTOM: EncryptionType.ValueType # 2 global___EncryptionType = EncryptionType +class _KeyDerivationFunction: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _KeyDerivationFunctionEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_KeyDerivationFunction.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PBKDF2: _KeyDerivationFunction.ValueType # 0 + HKDF: _KeyDerivationFunction.ValueType # 1 + +class KeyDerivationFunction(_KeyDerivationFunction, metaclass=_KeyDerivationFunctionEnumTypeWrapper): ... + +PBKDF2: KeyDerivationFunction.ValueType # 0 +HKDF: KeyDerivationFunction.ValueType # 1 +global___KeyDerivationFunction = KeyDerivationFunction + class _EncryptionState: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -74,7 +90,7 @@ KEY_RATCHETED: EncryptionState.ValueType # 5 INTERNAL_ERROR: EncryptionState.ValueType # 6 global___EncryptionState = EncryptionState -@typing_extensions.final +@typing.final class FrameCryptor(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -89,16 +105,17 @@ class FrameCryptor(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - track_sid: builtins.str = ..., - key_index: builtins.int = ..., - enabled: builtins.bool = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., + key_index: builtins.int | None = ..., + enabled: builtins.bool | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["enabled", b"enabled", "key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled", "key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___FrameCryptor = FrameCryptor -@typing_extensions.final +@typing.final class KeyProviderOptions(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -106,27 +123,32 @@ class KeyProviderOptions(google.protobuf.message.Message): RATCHET_WINDOW_SIZE_FIELD_NUMBER: builtins.int RATCHET_SALT_FIELD_NUMBER: builtins.int FAILURE_TOLERANCE_FIELD_NUMBER: builtins.int + KEY_RING_SIZE_FIELD_NUMBER: builtins.int + KEY_DERIVATION_FUNCTION_FIELD_NUMBER: builtins.int shared_key: builtins.bytes """Only specify if you want to use a shared_key""" ratchet_window_size: builtins.int ratchet_salt: builtins.bytes failure_tolerance: builtins.int - """-1 = no tolerence""" + """-1 = no tolerance""" + key_ring_size: builtins.int + key_derivation_function: global___KeyDerivationFunction.ValueType def __init__( self, *, shared_key: builtins.bytes | None = ..., - ratchet_window_size: builtins.int = ..., - ratchet_salt: builtins.bytes = ..., - failure_tolerance: builtins.int = ..., + ratchet_window_size: builtins.int | None = ..., + ratchet_salt: builtins.bytes | None = ..., + failure_tolerance: builtins.int | None = ..., + key_ring_size: builtins.int | None = ..., + key_derivation_function: global___KeyDerivationFunction.ValueType | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_shared_key", b"_shared_key", "shared_key", b"shared_key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_shared_key", b"_shared_key", "failure_tolerance", b"failure_tolerance", "ratchet_salt", b"ratchet_salt", "ratchet_window_size", b"ratchet_window_size", "shared_key", b"shared_key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_shared_key", b"_shared_key"]) -> typing_extensions.Literal["shared_key"] | None: ... + def HasField(self, field_name: typing.Literal["failure_tolerance", b"failure_tolerance", "key_derivation_function", b"key_derivation_function", "key_ring_size", b"key_ring_size", "ratchet_salt", b"ratchet_salt", "ratchet_window_size", b"ratchet_window_size", "shared_key", b"shared_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["failure_tolerance", b"failure_tolerance", "key_derivation_function", b"key_derivation_function", "key_ring_size", b"key_ring_size", "ratchet_salt", b"ratchet_salt", "ratchet_window_size", b"ratchet_window_size", "shared_key", b"shared_key"]) -> None: ... global___KeyProviderOptions = KeyProviderOptions -@typing_extensions.final +@typing.final class E2eeOptions(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -138,15 +160,15 @@ class E2eeOptions(google.protobuf.message.Message): def __init__( self, *, - encryption_type: global___EncryptionType.ValueType = ..., + encryption_type: global___EncryptionType.ValueType | None = ..., key_provider_options: global___KeyProviderOptions | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["key_provider_options", b"key_provider_options"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["encryption_type", b"encryption_type", "key_provider_options", b"key_provider_options"]) -> None: ... + def HasField(self, field_name: typing.Literal["encryption_type", b"encryption_type", "key_provider_options", b"key_provider_options"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["encryption_type", b"encryption_type", "key_provider_options", b"key_provider_options"]) -> None: ... global___E2eeOptions = E2eeOptions -@typing_extensions.final +@typing.final class E2eeManagerSetEnabledRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -155,13 +177,14 @@ class E2eeManagerSetEnabledRequest(google.protobuf.message.Message): def __init__( self, *, - enabled: builtins.bool = ..., + enabled: builtins.bool | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["enabled", b"enabled"]) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled"]) -> None: ... global___E2eeManagerSetEnabledRequest = E2eeManagerSetEnabledRequest -@typing_extensions.final +@typing.final class E2eeManagerSetEnabledResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -171,7 +194,7 @@ class E2eeManagerSetEnabledResponse(google.protobuf.message.Message): global___E2eeManagerSetEnabledResponse = E2eeManagerSetEnabledResponse -@typing_extensions.final +@typing.final class E2eeManagerGetFrameCryptorsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -181,7 +204,7 @@ class E2eeManagerGetFrameCryptorsRequest(google.protobuf.message.Message): global___E2eeManagerGetFrameCryptorsRequest = E2eeManagerGetFrameCryptorsRequest -@typing_extensions.final +@typing.final class E2eeManagerGetFrameCryptorsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -193,11 +216,11 @@ class E2eeManagerGetFrameCryptorsResponse(google.protobuf.message.Message): *, frame_cryptors: collections.abc.Iterable[global___FrameCryptor] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["frame_cryptors", b"frame_cryptors"]) -> None: ... + def ClearField(self, field_name: typing.Literal["frame_cryptors", b"frame_cryptors"]) -> None: ... global___E2eeManagerGetFrameCryptorsResponse = E2eeManagerGetFrameCryptorsResponse -@typing_extensions.final +@typing.final class FrameCryptorSetEnabledRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -210,15 +233,16 @@ class FrameCryptorSetEnabledRequest(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - track_sid: builtins.str = ..., - enabled: builtins.bool = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., + enabled: builtins.bool | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["enabled", b"enabled", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___FrameCryptorSetEnabledRequest = FrameCryptorSetEnabledRequest -@typing_extensions.final +@typing.final class FrameCryptorSetEnabledResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -228,7 +252,7 @@ class FrameCryptorSetEnabledResponse(google.protobuf.message.Message): global___FrameCryptorSetEnabledResponse = FrameCryptorSetEnabledResponse -@typing_extensions.final +@typing.final class FrameCryptorSetKeyIndexRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -241,15 +265,16 @@ class FrameCryptorSetKeyIndexRequest(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - track_sid: builtins.str = ..., - key_index: builtins.int = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___FrameCryptorSetKeyIndexRequest = FrameCryptorSetKeyIndexRequest -@typing_extensions.final +@typing.final class FrameCryptorSetKeyIndexResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -259,7 +284,7 @@ class FrameCryptorSetKeyIndexResponse(google.protobuf.message.Message): global___FrameCryptorSetKeyIndexResponse = FrameCryptorSetKeyIndexResponse -@typing_extensions.final +@typing.final class SetSharedKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -270,14 +295,15 @@ class SetSharedKeyRequest(google.protobuf.message.Message): def __init__( self, *, - shared_key: builtins.bytes = ..., - key_index: builtins.int = ..., + shared_key: builtins.bytes | None = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index", "shared_key", b"shared_key"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index", "shared_key", b"shared_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index", "shared_key", b"shared_key"]) -> None: ... global___SetSharedKeyRequest = SetSharedKeyRequest -@typing_extensions.final +@typing.final class SetSharedKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -287,7 +313,7 @@ class SetSharedKeyResponse(google.protobuf.message.Message): global___SetSharedKeyResponse = SetSharedKeyResponse -@typing_extensions.final +@typing.final class RatchetSharedKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -296,13 +322,14 @@ class RatchetSharedKeyRequest(google.protobuf.message.Message): def __init__( self, *, - key_index: builtins.int = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index"]) -> None: ... global___RatchetSharedKeyRequest = RatchetSharedKeyRequest -@typing_extensions.final +@typing.final class RatchetSharedKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -313,13 +340,12 @@ class RatchetSharedKeyResponse(google.protobuf.message.Message): *, new_key: builtins.bytes | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_new_key", b"_new_key", "new_key", b"new_key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_new_key", b"_new_key", "new_key", b"new_key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_new_key", b"_new_key"]) -> typing_extensions.Literal["new_key"] | None: ... + def HasField(self, field_name: typing.Literal["new_key", b"new_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["new_key", b"new_key"]) -> None: ... global___RatchetSharedKeyResponse = RatchetSharedKeyResponse -@typing_extensions.final +@typing.final class GetSharedKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -328,13 +354,14 @@ class GetSharedKeyRequest(google.protobuf.message.Message): def __init__( self, *, - key_index: builtins.int = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index"]) -> None: ... global___GetSharedKeyRequest = GetSharedKeyRequest -@typing_extensions.final +@typing.final class GetSharedKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -345,13 +372,12 @@ class GetSharedKeyResponse(google.protobuf.message.Message): *, key: builtins.bytes | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_key", b"_key", "key", b"key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_key", b"_key", "key", b"key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_key", b"_key"]) -> typing_extensions.Literal["key"] | None: ... + def HasField(self, field_name: typing.Literal["key", b"key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key"]) -> None: ... global___GetSharedKeyResponse = GetSharedKeyResponse -@typing_extensions.final +@typing.final class SetKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -364,15 +390,16 @@ class SetKeyRequest(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - key: builtins.bytes = ..., - key_index: builtins.int = ..., + participant_identity: builtins.str | None = ..., + key: builtins.bytes | None = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "key_index", b"key_index", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... global___SetKeyRequest = SetKeyRequest -@typing_extensions.final +@typing.final class SetKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -382,7 +409,7 @@ class SetKeyResponse(google.protobuf.message.Message): global___SetKeyResponse = SetKeyResponse -@typing_extensions.final +@typing.final class RatchetKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -393,14 +420,15 @@ class RatchetKeyRequest(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - key_index: builtins.int = ..., + participant_identity: builtins.str | None = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... global___RatchetKeyRequest = RatchetKeyRequest -@typing_extensions.final +@typing.final class RatchetKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -411,13 +439,12 @@ class RatchetKeyResponse(google.protobuf.message.Message): *, new_key: builtins.bytes | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_new_key", b"_new_key", "new_key", b"new_key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_new_key", b"_new_key", "new_key", b"new_key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_new_key", b"_new_key"]) -> typing_extensions.Literal["new_key"] | None: ... + def HasField(self, field_name: typing.Literal["new_key", b"new_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["new_key", b"new_key"]) -> None: ... global___RatchetKeyResponse = RatchetKeyResponse -@typing_extensions.final +@typing.final class GetKeyRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -428,14 +455,15 @@ class GetKeyRequest(google.protobuf.message.Message): def __init__( self, *, - participant_identity: builtins.str = ..., - key_index: builtins.int = ..., + participant_identity: builtins.str | None = ..., + key_index: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... + def HasField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key_index", b"key_index", "participant_identity", b"participant_identity"]) -> None: ... global___GetKeyRequest = GetKeyRequest -@typing_extensions.final +@typing.final class GetKeyResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -446,13 +474,12 @@ class GetKeyResponse(google.protobuf.message.Message): *, key: builtins.bytes | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_key", b"_key", "key", b"key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_key", b"_key", "key", b"key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_key", b"_key"]) -> typing_extensions.Literal["key"] | None: ... + def HasField(self, field_name: typing.Literal["key", b"key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key"]) -> None: ... global___GetKeyResponse = GetKeyResponse -@typing_extensions.final +@typing.final class E2eeRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -491,7 +518,7 @@ class E2eeRequest(google.protobuf.message.Message): def __init__( self, *, - room_handle: builtins.int = ..., + room_handle: builtins.int | None = ..., manager_set_enabled: global___E2eeManagerSetEnabledRequest | None = ..., manager_get_frame_cryptors: global___E2eeManagerGetFrameCryptorsRequest | None = ..., cryptor_set_enabled: global___FrameCryptorSetEnabledRequest | None = ..., @@ -503,13 +530,13 @@ class E2eeRequest(google.protobuf.message.Message): ratchet_key: global___RatchetKeyRequest | None = ..., get_key: global___GetKeyRequest | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "room_handle", b"room_handle", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["manager_set_enabled", "manager_get_frame_cryptors", "cryptor_set_enabled", "cryptor_set_key_index", "set_shared_key", "ratchet_shared_key", "get_shared_key", "set_key", "ratchet_key", "get_key"] | None: ... + def HasField(self, field_name: typing.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "room_handle", b"room_handle", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "room_handle", b"room_handle", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["manager_set_enabled", "manager_get_frame_cryptors", "cryptor_set_enabled", "cryptor_set_key_index", "set_shared_key", "ratchet_shared_key", "get_shared_key", "set_key", "ratchet_key", "get_key"] | None: ... global___E2eeRequest = E2eeRequest -@typing_extensions.final +@typing.final class E2eeResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -557,8 +584,8 @@ class E2eeResponse(google.protobuf.message.Message): ratchet_key: global___RatchetKeyResponse | None = ..., get_key: global___GetKeyResponse | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["manager_set_enabled", "manager_get_frame_cryptors", "cryptor_set_enabled", "cryptor_set_key_index", "set_shared_key", "ratchet_shared_key", "get_shared_key", "set_key", "ratchet_key", "get_key"] | None: ... + def HasField(self, field_name: typing.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["cryptor_set_enabled", b"cryptor_set_enabled", "cryptor_set_key_index", b"cryptor_set_key_index", "get_key", b"get_key", "get_shared_key", b"get_shared_key", "manager_get_frame_cryptors", b"manager_get_frame_cryptors", "manager_set_enabled", b"manager_set_enabled", "message", b"message", "ratchet_key", b"ratchet_key", "ratchet_shared_key", b"ratchet_shared_key", "set_key", b"set_key", "set_shared_key", b"set_shared_key"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["manager_set_enabled", "manager_get_frame_cryptors", "cryptor_set_enabled", "cryptor_set_key_index", "set_shared_key", "ratchet_shared_key", "get_shared_key", "set_key", "ratchet_key", "get_key"] | None: ... global___E2eeResponse = E2eeResponse diff --git a/livekit-rtc/livekit/rtc/_proto/ffi_pb2.py b/livekit-rtc/livekit/rtc/_proto/ffi_pb2.py index e414f0ca..343a9a00 100644 --- a/livekit-rtc/livekit/rtc/_proto/ffi_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/ffi_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: ffi.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -12,14 +12,18 @@ _sym_db = _symbol_database.Default() +from . import audio_frame_pb2 as audio__frame__pb2 +from . import data_stream_pb2 as data__stream__pb2 +from . import data_track_pb2 as data__track__pb2 from . import e2ee_pb2 as e2ee__pb2 -from . import track_pb2 as track__pb2 from . import room_pb2 as room__pb2 +from . import rpc_pb2 as rpc__pb2 +from . import track_pb2 as track__pb2 +from . import track_publication_pb2 as track__publication__pb2 from . import video_frame_pb2 as video__frame__pb2 -from . import audio_frame_pb2 as audio__frame__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tffi.proto\x12\rlivekit.proto\x1a\ne2ee.proto\x1a\x0btrack.proto\x1a\nroom.proto\x1a\x11video_frame.proto\x1a\x11\x61udio_frame.proto\"\x9c\x0c\n\nFfiRequest\x12\x30\n\x07\x64ispose\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.DisposeRequestH\x00\x12\x30\n\x07\x63onnect\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.ConnectRequestH\x00\x12\x36\n\ndisconnect\x18\x04 \x01(\x0b\x32 .livekit.proto.DisconnectRequestH\x00\x12;\n\rpublish_track\x18\x05 \x01(\x0b\x32\".livekit.proto.PublishTrackRequestH\x00\x12?\n\x0funpublish_track\x18\x06 \x01(\x0b\x32$.livekit.proto.UnpublishTrackRequestH\x00\x12\x39\n\x0cpublish_data\x18\x07 \x01(\x0b\x32!.livekit.proto.PublishDataRequestH\x00\x12=\n\x0eset_subscribed\x18\x08 \x01(\x0b\x32#.livekit.proto.SetSubscribedRequestH\x00\x12J\n\x15update_local_metadata\x18\t \x01(\x0b\x32).livekit.proto.UpdateLocalMetadataRequestH\x00\x12\x42\n\x11update_local_name\x18\n \x01(\x0b\x32%.livekit.proto.UpdateLocalNameRequestH\x00\x12\x42\n\x11get_session_stats\x18\x0b \x01(\x0b\x32%.livekit.proto.GetSessionStatsRequestH\x00\x12K\n\x15publish_transcription\x18\x0c \x01(\x0b\x32*.livekit.proto.PublishTranscriptionRequestH\x00\x12\x44\n\x12\x63reate_video_track\x18\r \x01(\x0b\x32&.livekit.proto.CreateVideoTrackRequestH\x00\x12\x44\n\x12\x63reate_audio_track\x18\x0e \x01(\x0b\x32&.livekit.proto.CreateAudioTrackRequestH\x00\x12\x33\n\tget_stats\x18\x0f \x01(\x0b\x32\x1e.livekit.proto.GetStatsRequestH\x00\x12@\n\x10new_video_stream\x18\x10 \x01(\x0b\x32$.livekit.proto.NewVideoStreamRequestH\x00\x12@\n\x10new_video_source\x18\x11 \x01(\x0b\x32$.livekit.proto.NewVideoSourceRequestH\x00\x12\x46\n\x13\x63\x61pture_video_frame\x18\x12 \x01(\x0b\x32\'.livekit.proto.CaptureVideoFrameRequestH\x00\x12;\n\rvideo_convert\x18\x13 \x01(\x0b\x32\".livekit.proto.VideoConvertRequestH\x00\x12@\n\x10new_audio_stream\x18\x16 \x01(\x0b\x32$.livekit.proto.NewAudioStreamRequestH\x00\x12@\n\x10new_audio_source\x18\x17 \x01(\x0b\x32$.livekit.proto.NewAudioSourceRequestH\x00\x12\x46\n\x13\x63\x61pture_audio_frame\x18\x18 \x01(\x0b\x32\'.livekit.proto.CaptureAudioFrameRequestH\x00\x12\x46\n\x13new_audio_resampler\x18\x19 \x01(\x0b\x32\'.livekit.proto.NewAudioResamplerRequestH\x00\x12\x44\n\x12remix_and_resample\x18\x1a \x01(\x0b\x32&.livekit.proto.RemixAndResampleRequestH\x00\x12*\n\x04\x65\x32\x65\x65\x18\x1b \x01(\x0b\x32\x1a.livekit.proto.E2eeRequestH\x00\x42\t\n\x07message\"\xb5\x0c\n\x0b\x46\x66iResponse\x12\x31\n\x07\x64ispose\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.DisposeResponseH\x00\x12\x31\n\x07\x63onnect\x18\x03 \x01(\x0b\x32\x1e.livekit.proto.ConnectResponseH\x00\x12\x37\n\ndisconnect\x18\x04 \x01(\x0b\x32!.livekit.proto.DisconnectResponseH\x00\x12<\n\rpublish_track\x18\x05 \x01(\x0b\x32#.livekit.proto.PublishTrackResponseH\x00\x12@\n\x0funpublish_track\x18\x06 \x01(\x0b\x32%.livekit.proto.UnpublishTrackResponseH\x00\x12:\n\x0cpublish_data\x18\x07 \x01(\x0b\x32\".livekit.proto.PublishDataResponseH\x00\x12>\n\x0eset_subscribed\x18\x08 \x01(\x0b\x32$.livekit.proto.SetSubscribedResponseH\x00\x12K\n\x15update_local_metadata\x18\t \x01(\x0b\x32*.livekit.proto.UpdateLocalMetadataResponseH\x00\x12\x43\n\x11update_local_name\x18\n \x01(\x0b\x32&.livekit.proto.UpdateLocalNameResponseH\x00\x12\x43\n\x11get_session_stats\x18\x0b \x01(\x0b\x32&.livekit.proto.GetSessionStatsResponseH\x00\x12L\n\x15publish_transcription\x18\x0c \x01(\x0b\x32+.livekit.proto.PublishTranscriptionResponseH\x00\x12\x45\n\x12\x63reate_video_track\x18\r \x01(\x0b\x32\'.livekit.proto.CreateVideoTrackResponseH\x00\x12\x45\n\x12\x63reate_audio_track\x18\x0e \x01(\x0b\x32\'.livekit.proto.CreateAudioTrackResponseH\x00\x12\x34\n\tget_stats\x18\x0f \x01(\x0b\x32\x1f.livekit.proto.GetStatsResponseH\x00\x12\x41\n\x10new_video_stream\x18\x10 \x01(\x0b\x32%.livekit.proto.NewVideoStreamResponseH\x00\x12\x41\n\x10new_video_source\x18\x11 \x01(\x0b\x32%.livekit.proto.NewVideoSourceResponseH\x00\x12G\n\x13\x63\x61pture_video_frame\x18\x12 \x01(\x0b\x32(.livekit.proto.CaptureVideoFrameResponseH\x00\x12<\n\rvideo_convert\x18\x13 \x01(\x0b\x32#.livekit.proto.VideoConvertResponseH\x00\x12\x41\n\x10new_audio_stream\x18\x16 \x01(\x0b\x32%.livekit.proto.NewAudioStreamResponseH\x00\x12\x41\n\x10new_audio_source\x18\x17 \x01(\x0b\x32%.livekit.proto.NewAudioSourceResponseH\x00\x12G\n\x13\x63\x61pture_audio_frame\x18\x18 \x01(\x0b\x32(.livekit.proto.CaptureAudioFrameResponseH\x00\x12G\n\x13new_audio_resampler\x18\x19 \x01(\x0b\x32(.livekit.proto.NewAudioResamplerResponseH\x00\x12\x45\n\x12remix_and_resample\x18\x1a \x01(\x0b\x32\'.livekit.proto.RemixAndResampleResponseH\x00\x12+\n\x04\x65\x32\x65\x65\x18\x1b \x01(\x0b\x32\x1b.livekit.proto.E2eeResponseH\x00\x42\t\n\x07message\"\xc4\x08\n\x08\x46\x66iEvent\x12.\n\nroom_event\x18\x01 \x01(\x0b\x32\x18.livekit.proto.RoomEventH\x00\x12\x30\n\x0btrack_event\x18\x02 \x01(\x0b\x32\x19.livekit.proto.TrackEventH\x00\x12=\n\x12video_stream_event\x18\x03 \x01(\x0b\x32\x1f.livekit.proto.VideoStreamEventH\x00\x12=\n\x12\x61udio_stream_event\x18\x04 \x01(\x0b\x32\x1f.livekit.proto.AudioStreamEventH\x00\x12\x31\n\x07\x63onnect\x18\x05 \x01(\x0b\x32\x1e.livekit.proto.ConnectCallbackH\x00\x12\x37\n\ndisconnect\x18\x06 \x01(\x0b\x32!.livekit.proto.DisconnectCallbackH\x00\x12\x31\n\x07\x64ispose\x18\x07 \x01(\x0b\x32\x1e.livekit.proto.DisposeCallbackH\x00\x12<\n\rpublish_track\x18\x08 \x01(\x0b\x32#.livekit.proto.PublishTrackCallbackH\x00\x12@\n\x0funpublish_track\x18\t \x01(\x0b\x32%.livekit.proto.UnpublishTrackCallbackH\x00\x12:\n\x0cpublish_data\x18\n \x01(\x0b\x32\".livekit.proto.PublishDataCallbackH\x00\x12L\n\x15publish_transcription\x18\x0b \x01(\x0b\x32+.livekit.proto.PublishTranscriptionCallbackH\x00\x12G\n\x13\x63\x61pture_audio_frame\x18\x0c \x01(\x0b\x32(.livekit.proto.CaptureAudioFrameCallbackH\x00\x12K\n\x15update_local_metadata\x18\r \x01(\x0b\x32*.livekit.proto.UpdateLocalMetadataCallbackH\x00\x12\x43\n\x11update_local_name\x18\x0e \x01(\x0b\x32&.livekit.proto.UpdateLocalNameCallbackH\x00\x12\x34\n\tget_stats\x18\x0f \x01(\x0b\x32\x1f.livekit.proto.GetStatsCallbackH\x00\x12\'\n\x04logs\x18\x10 \x01(\x0b\x32\x17.livekit.proto.LogBatchH\x00\x12\x43\n\x11get_session_stats\x18\x11 \x01(\x0b\x32&.livekit.proto.GetSessionStatsCallbackH\x00\x12%\n\x05panic\x18\x12 \x01(\x0b\x32\x14.livekit.proto.PanicH\x00\x42\t\n\x07message\"\x1f\n\x0e\x44isposeRequest\x12\r\n\x05\x61sync\x18\x01 \x01(\x08\"5\n\x0f\x44isposeResponse\x12\x15\n\x08\x61sync_id\x18\x01 \x01(\x04H\x00\x88\x01\x01\x42\x0b\n\t_async_id\"#\n\x0f\x44isposeCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"\xb6\x01\n\tLogRecord\x12&\n\x05level\x18\x01 \x01(\x0e\x32\x17.livekit.proto.LogLevel\x12\x0e\n\x06target\x18\x02 \x01(\t\x12\x18\n\x0bmodule_path\x18\x03 \x01(\tH\x00\x88\x01\x01\x12\x11\n\x04\x66ile\x18\x04 \x01(\tH\x01\x88\x01\x01\x12\x11\n\x04line\x18\x05 \x01(\rH\x02\x88\x01\x01\x12\x0f\n\x07message\x18\x06 \x01(\tB\x0e\n\x0c_module_pathB\x07\n\x05_fileB\x07\n\x05_line\"5\n\x08LogBatch\x12)\n\x07records\x18\x01 \x03(\x0b\x32\x18.livekit.proto.LogRecord\"\x18\n\x05Panic\x12\x0f\n\x07message\x18\x01 \x01(\t*S\n\x08LogLevel\x12\r\n\tLOG_ERROR\x10\x00\x12\x0c\n\x08LOG_WARN\x10\x01\x12\x0c\n\x08LOG_INFO\x10\x02\x12\r\n\tLOG_DEBUG\x10\x03\x12\r\n\tLOG_TRACE\x10\x04\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\tffi.proto\x12\rlivekit.proto\x1a\x11\x61udio_frame.proto\x1a\x11\x64\x61ta_stream.proto\x1a\x10\x64\x61ta_track.proto\x1a\ne2ee.proto\x1a\nroom.proto\x1a\trpc.proto\x1a\x0btrack.proto\x1a\x17track_publication.proto\x1a\x11video_frame.proto\"\xa7\x30\n\nFfiRequest\x12\x30\n\x07\x64ispose\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.DisposeRequestH\x00\x12\x30\n\x07\x63onnect\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.ConnectRequestH\x00\x12\x36\n\ndisconnect\x18\x04 \x01(\x0b\x32 .livekit.proto.DisconnectRequestH\x00\x12;\n\rpublish_track\x18\x05 \x01(\x0b\x32\".livekit.proto.PublishTrackRequestH\x00\x12?\n\x0funpublish_track\x18\x06 \x01(\x0b\x32$.livekit.proto.UnpublishTrackRequestH\x00\x12\x39\n\x0cpublish_data\x18\x07 \x01(\x0b\x32!.livekit.proto.PublishDataRequestH\x00\x12=\n\x0eset_subscribed\x18\x08 \x01(\x0b\x32#.livekit.proto.SetSubscribedRequestH\x00\x12\x44\n\x12set_local_metadata\x18\t \x01(\x0b\x32&.livekit.proto.SetLocalMetadataRequestH\x00\x12<\n\x0eset_local_name\x18\n \x01(\x0b\x32\".livekit.proto.SetLocalNameRequestH\x00\x12H\n\x14set_local_attributes\x18\x0b \x01(\x0b\x32(.livekit.proto.SetLocalAttributesRequestH\x00\x12\x42\n\x11get_session_stats\x18\x0c \x01(\x0b\x32%.livekit.proto.GetSessionStatsRequestH\x00\x12K\n\x15publish_transcription\x18\r \x01(\x0b\x32*.livekit.proto.PublishTranscriptionRequestH\x00\x12@\n\x10publish_sip_dtmf\x18\x0e \x01(\x0b\x32$.livekit.proto.PublishSipDtmfRequestH\x00\x12\x44\n\x12\x63reate_video_track\x18\x0f \x01(\x0b\x32&.livekit.proto.CreateVideoTrackRequestH\x00\x12\x44\n\x12\x63reate_audio_track\x18\x10 \x01(\x0b\x32&.livekit.proto.CreateAudioTrackRequestH\x00\x12@\n\x10local_track_mute\x18\x11 \x01(\x0b\x32$.livekit.proto.LocalTrackMuteRequestH\x00\x12\x46\n\x13\x65nable_remote_track\x18\x12 \x01(\x0b\x32\'.livekit.proto.EnableRemoteTrackRequestH\x00\x12\x33\n\tget_stats\x18\x13 \x01(\x0b\x32\x1e.livekit.proto.GetStatsRequestH\x00\x12\x63\n\"set_track_subscription_permissions\x18\x30 \x01(\x0b\x32\x35.livekit.proto.SetTrackSubscriptionPermissionsRequestH\x00\x12@\n\x10new_video_stream\x18\x14 \x01(\x0b\x32$.livekit.proto.NewVideoStreamRequestH\x00\x12@\n\x10new_video_source\x18\x15 \x01(\x0b\x32$.livekit.proto.NewVideoSourceRequestH\x00\x12\x46\n\x13\x63\x61pture_video_frame\x18\x16 \x01(\x0b\x32\'.livekit.proto.CaptureVideoFrameRequestH\x00\x12;\n\rvideo_convert\x18\x17 \x01(\x0b\x32\".livekit.proto.VideoConvertRequestH\x00\x12Y\n\x1dvideo_stream_from_participant\x18\x18 \x01(\x0b\x32\x30.livekit.proto.VideoStreamFromParticipantRequestH\x00\x12@\n\x10new_audio_stream\x18\x19 \x01(\x0b\x32$.livekit.proto.NewAudioStreamRequestH\x00\x12@\n\x10new_audio_source\x18\x1a \x01(\x0b\x32$.livekit.proto.NewAudioSourceRequestH\x00\x12\x46\n\x13\x63\x61pture_audio_frame\x18\x1b \x01(\x0b\x32\'.livekit.proto.CaptureAudioFrameRequestH\x00\x12\x44\n\x12\x63lear_audio_buffer\x18\x1c \x01(\x0b\x32&.livekit.proto.ClearAudioBufferRequestH\x00\x12\x46\n\x13new_audio_resampler\x18\x1d \x01(\x0b\x32\'.livekit.proto.NewAudioResamplerRequestH\x00\x12\x44\n\x12remix_and_resample\x18\x1e \x01(\x0b\x32&.livekit.proto.RemixAndResampleRequestH\x00\x12*\n\x04\x65\x32\x65\x65\x18\x1f \x01(\x0b\x32\x1a.livekit.proto.E2eeRequestH\x00\x12Y\n\x1d\x61udio_stream_from_participant\x18 \x01(\x0b\x32\x30.livekit.proto.AudioStreamFromParticipantRequestH\x00\x12\x42\n\x11new_sox_resampler\x18! \x01(\x0b\x32%.livekit.proto.NewSoxResamplerRequestH\x00\x12\x44\n\x12push_sox_resampler\x18\" \x01(\x0b\x32&.livekit.proto.PushSoxResamplerRequestH\x00\x12\x46\n\x13\x66lush_sox_resampler\x18# \x01(\x0b\x32\'.livekit.proto.FlushSoxResamplerRequestH\x00\x12\x42\n\x11send_chat_message\x18$ \x01(\x0b\x32%.livekit.proto.SendChatMessageRequestH\x00\x12\x42\n\x11\x65\x64it_chat_message\x18% \x01(\x0b\x32%.livekit.proto.EditChatMessageRequestH\x00\x12\x37\n\x0bperform_rpc\x18& \x01(\x0b\x32 .livekit.proto.PerformRpcRequestH\x00\x12\x46\n\x13register_rpc_method\x18\' \x01(\x0b\x32\'.livekit.proto.RegisterRpcMethodRequestH\x00\x12J\n\x15unregister_rpc_method\x18( \x01(\x0b\x32).livekit.proto.UnregisterRpcMethodRequestH\x00\x12[\n\x1erpc_method_invocation_response\x18) \x01(\x0b\x32\x31.livekit.proto.RpcMethodInvocationResponseRequestH\x00\x12]\n\x1f\x65nable_remote_track_publication\x18* \x01(\x0b\x32\x32.livekit.proto.EnableRemoteTrackPublicationRequestH\x00\x12p\n)update_remote_track_publication_dimension\x18+ \x01(\x0b\x32;.livekit.proto.UpdateRemoteTrackPublicationDimensionRequestH\x00\x12\x44\n\x12send_stream_header\x18, \x01(\x0b\x32&.livekit.proto.SendStreamHeaderRequestH\x00\x12\x42\n\x11send_stream_chunk\x18- \x01(\x0b\x32%.livekit.proto.SendStreamChunkRequestH\x00\x12\x46\n\x13send_stream_trailer\x18. \x01(\x0b\x32\'.livekit.proto.SendStreamTrailerRequestH\x00\x12x\n.set_data_channel_buffered_amount_low_threshold\x18/ \x01(\x0b\x32>.livekit.proto.SetDataChannelBufferedAmountLowThresholdRequestH\x00\x12O\n\x18load_audio_filter_plugin\x18\x31 \x01(\x0b\x32+.livekit.proto.LoadAudioFilterPluginRequestH\x00\x12/\n\x07new_apm\x18\x32 \x01(\x0b\x32\x1c.livekit.proto.NewApmRequestH\x00\x12\x44\n\x12\x61pm_process_stream\x18\x33 \x01(\x0b\x32&.livekit.proto.ApmProcessStreamRequestH\x00\x12S\n\x1a\x61pm_process_reverse_stream\x18\x34 \x01(\x0b\x32-.livekit.proto.ApmProcessReverseStreamRequestH\x00\x12G\n\x14\x61pm_set_stream_delay\x18\x35 \x01(\x0b\x32\'.livekit.proto.ApmSetStreamDelayRequestH\x00\x12V\n\x15\x62yte_read_incremental\x18\x36 \x01(\x0b\x32\x35.livekit.proto.ByteStreamReaderReadIncrementalRequestH\x00\x12\x46\n\rbyte_read_all\x18\x37 \x01(\x0b\x32-.livekit.proto.ByteStreamReaderReadAllRequestH\x00\x12O\n\x12\x62yte_write_to_file\x18\x38 \x01(\x0b\x32\x31.livekit.proto.ByteStreamReaderWriteToFileRequestH\x00\x12V\n\x15text_read_incremental\x18\x39 \x01(\x0b\x32\x35.livekit.proto.TextStreamReaderReadIncrementalRequestH\x00\x12\x46\n\rtext_read_all\x18: \x01(\x0b\x32-.livekit.proto.TextStreamReaderReadAllRequestH\x00\x12\x39\n\tsend_file\x18; \x01(\x0b\x32$.livekit.proto.StreamSendFileRequestH\x00\x12\x39\n\tsend_text\x18< \x01(\x0b\x32$.livekit.proto.StreamSendTextRequestH\x00\x12@\n\x10\x62yte_stream_open\x18= \x01(\x0b\x32$.livekit.proto.ByteStreamOpenRequestH\x00\x12H\n\x11\x62yte_stream_write\x18> \x01(\x0b\x32+.livekit.proto.ByteStreamWriterWriteRequestH\x00\x12H\n\x11\x62yte_stream_close\x18? \x01(\x0b\x32+.livekit.proto.ByteStreamWriterCloseRequestH\x00\x12@\n\x10text_stream_open\x18@ \x01(\x0b\x32$.livekit.proto.TextStreamOpenRequestH\x00\x12H\n\x11text_stream_write\x18\x41 \x01(\x0b\x32+.livekit.proto.TextStreamWriterWriteRequestH\x00\x12H\n\x11text_stream_close\x18\x42 \x01(\x0b\x32+.livekit.proto.TextStreamWriterCloseRequestH\x00\x12;\n\nsend_bytes\x18\x43 \x01(\x0b\x32%.livekit.proto.StreamSendBytesRequestH\x00\x12\x66\n$set_remote_track_publication_quality\x18\x44 \x01(\x0b\x32\x36.livekit.proto.SetRemoteTrackPublicationQualityRequestH\x00\x12\x44\n\x12publish_data_track\x18\x45 \x01(\x0b\x32&.livekit.proto.PublishDataTrackRequestH\x00\x12P\n\x19local_data_track_try_push\x18\x46 \x01(\x0b\x32+.livekit.proto.LocalDataTrackTryPushRequestH\x00\x12S\n\x1alocal_data_track_unpublish\x18G \x01(\x0b\x32-.livekit.proto.LocalDataTrackUnpublishRequestH\x00\x12X\n\x1dlocal_data_track_is_published\x18H \x01(\x0b\x32/.livekit.proto.LocalDataTrackIsPublishedRequestH\x00\x12H\n\x14subscribe_data_track\x18I \x01(\x0b\x32(.livekit.proto.SubscribeDataTrackRequestH\x00\x12Z\n\x1eremote_data_track_is_published\x18J \x01(\x0b\x32\x30.livekit.proto.RemoteDataTrackIsPublishedRequestH\x00\x12K\n\x16\x64\x61ta_track_stream_read\x18K \x01(\x0b\x32).livekit.proto.DataTrackStreamReadRequestH\x00\x12i\n&remote_data_track_set_pipeline_options\x18T \x01(\x0b\x32\x37.livekit.proto.RemoteDataTrackSetPipelineOptionsRequestH\x00\x12;\n\rdefine_schema\x18U \x01(\x0b\x32\".livekit.proto.DefineSchemaRequestH\x00\x12\x35\n\nget_schema\x18V \x01(\x0b\x32\x1f.livekit.proto.GetSchemaRequestH\x00\x12\x43\n\x11simulate_scenario\x18L \x01(\x0b\x32&.livekit.proto.SimulateScenarioRequestH\x00\x12\x44\n\x12new_platform_audio\x18M \x01(\x0b\x32&.livekit.proto.NewPlatformAudioRequestH\x00\x12\x42\n\x11get_audio_devices\x18N \x01(\x0b\x32%.livekit.proto.GetAudioDevicesRequestH\x00\x12H\n\x14set_recording_device\x18O \x01(\x0b\x32(.livekit.proto.SetRecordingDeviceRequestH\x00\x12\x44\n\x12set_playout_device\x18P \x01(\x0b\x32&.livekit.proto.SetPlayoutDeviceRequestH\x00\x12?\n\x0fstart_recording\x18Q \x01(\x0b\x32$.livekit.proto.StartRecordingRequestH\x00\x12=\n\x0estop_recording\x18R \x01(\x0b\x32#.livekit.proto.StopRecordingRequestH\x00\x12G\n\x14ready_for_room_event\x18S \x01(\x0b\x32\'.livekit.proto.ReadyForRoomEventRequestH\x00\x42\t\n\x07message\"\xb8\x30\n\x0b\x46\x66iResponse\x12\x31\n\x07\x64ispose\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.DisposeResponseH\x00\x12\x31\n\x07\x63onnect\x18\x03 \x01(\x0b\x32\x1e.livekit.proto.ConnectResponseH\x00\x12\x37\n\ndisconnect\x18\x04 \x01(\x0b\x32!.livekit.proto.DisconnectResponseH\x00\x12<\n\rpublish_track\x18\x05 \x01(\x0b\x32#.livekit.proto.PublishTrackResponseH\x00\x12@\n\x0funpublish_track\x18\x06 \x01(\x0b\x32%.livekit.proto.UnpublishTrackResponseH\x00\x12:\n\x0cpublish_data\x18\x07 \x01(\x0b\x32\".livekit.proto.PublishDataResponseH\x00\x12>\n\x0eset_subscribed\x18\x08 \x01(\x0b\x32$.livekit.proto.SetSubscribedResponseH\x00\x12\x45\n\x12set_local_metadata\x18\t \x01(\x0b\x32\'.livekit.proto.SetLocalMetadataResponseH\x00\x12=\n\x0eset_local_name\x18\n \x01(\x0b\x32#.livekit.proto.SetLocalNameResponseH\x00\x12I\n\x14set_local_attributes\x18\x0b \x01(\x0b\x32).livekit.proto.SetLocalAttributesResponseH\x00\x12\x43\n\x11get_session_stats\x18\x0c \x01(\x0b\x32&.livekit.proto.GetSessionStatsResponseH\x00\x12L\n\x15publish_transcription\x18\r \x01(\x0b\x32+.livekit.proto.PublishTranscriptionResponseH\x00\x12\x41\n\x10publish_sip_dtmf\x18\x0e \x01(\x0b\x32%.livekit.proto.PublishSipDtmfResponseH\x00\x12\x45\n\x12\x63reate_video_track\x18\x0f \x01(\x0b\x32\'.livekit.proto.CreateVideoTrackResponseH\x00\x12\x45\n\x12\x63reate_audio_track\x18\x10 \x01(\x0b\x32\'.livekit.proto.CreateAudioTrackResponseH\x00\x12\x41\n\x10local_track_mute\x18\x11 \x01(\x0b\x32%.livekit.proto.LocalTrackMuteResponseH\x00\x12G\n\x13\x65nable_remote_track\x18\x12 \x01(\x0b\x32(.livekit.proto.EnableRemoteTrackResponseH\x00\x12\x34\n\tget_stats\x18\x13 \x01(\x0b\x32\x1f.livekit.proto.GetStatsResponseH\x00\x12\x64\n\"set_track_subscription_permissions\x18/ \x01(\x0b\x32\x36.livekit.proto.SetTrackSubscriptionPermissionsResponseH\x00\x12\x41\n\x10new_video_stream\x18\x14 \x01(\x0b\x32%.livekit.proto.NewVideoStreamResponseH\x00\x12\x41\n\x10new_video_source\x18\x15 \x01(\x0b\x32%.livekit.proto.NewVideoSourceResponseH\x00\x12G\n\x13\x63\x61pture_video_frame\x18\x16 \x01(\x0b\x32(.livekit.proto.CaptureVideoFrameResponseH\x00\x12<\n\rvideo_convert\x18\x17 \x01(\x0b\x32#.livekit.proto.VideoConvertResponseH\x00\x12Z\n\x1dvideo_stream_from_participant\x18\x18 \x01(\x0b\x32\x31.livekit.proto.VideoStreamFromParticipantResponseH\x00\x12\x41\n\x10new_audio_stream\x18\x19 \x01(\x0b\x32%.livekit.proto.NewAudioStreamResponseH\x00\x12\x41\n\x10new_audio_source\x18\x1a \x01(\x0b\x32%.livekit.proto.NewAudioSourceResponseH\x00\x12G\n\x13\x63\x61pture_audio_frame\x18\x1b \x01(\x0b\x32(.livekit.proto.CaptureAudioFrameResponseH\x00\x12\x45\n\x12\x63lear_audio_buffer\x18\x1c \x01(\x0b\x32\'.livekit.proto.ClearAudioBufferResponseH\x00\x12G\n\x13new_audio_resampler\x18\x1d \x01(\x0b\x32(.livekit.proto.NewAudioResamplerResponseH\x00\x12\x45\n\x12remix_and_resample\x18\x1e \x01(\x0b\x32\'.livekit.proto.RemixAndResampleResponseH\x00\x12Z\n\x1d\x61udio_stream_from_participant\x18\x1f \x01(\x0b\x32\x31.livekit.proto.AudioStreamFromParticipantResponseH\x00\x12+\n\x04\x65\x32\x65\x65\x18 \x01(\x0b\x32\x1b.livekit.proto.E2eeResponseH\x00\x12\x43\n\x11new_sox_resampler\x18! \x01(\x0b\x32&.livekit.proto.NewSoxResamplerResponseH\x00\x12\x45\n\x12push_sox_resampler\x18\" \x01(\x0b\x32\'.livekit.proto.PushSoxResamplerResponseH\x00\x12G\n\x13\x66lush_sox_resampler\x18# \x01(\x0b\x32(.livekit.proto.FlushSoxResamplerResponseH\x00\x12\x43\n\x11send_chat_message\x18$ \x01(\x0b\x32&.livekit.proto.SendChatMessageResponseH\x00\x12\x38\n\x0bperform_rpc\x18% \x01(\x0b\x32!.livekit.proto.PerformRpcResponseH\x00\x12G\n\x13register_rpc_method\x18& \x01(\x0b\x32(.livekit.proto.RegisterRpcMethodResponseH\x00\x12K\n\x15unregister_rpc_method\x18\' \x01(\x0b\x32*.livekit.proto.UnregisterRpcMethodResponseH\x00\x12\\\n\x1erpc_method_invocation_response\x18( \x01(\x0b\x32\x32.livekit.proto.RpcMethodInvocationResponseResponseH\x00\x12^\n\x1f\x65nable_remote_track_publication\x18) \x01(\x0b\x32\x33.livekit.proto.EnableRemoteTrackPublicationResponseH\x00\x12q\n)update_remote_track_publication_dimension\x18* \x01(\x0b\x32<.livekit.proto.UpdateRemoteTrackPublicationDimensionResponseH\x00\x12\x45\n\x12send_stream_header\x18+ \x01(\x0b\x32\'.livekit.proto.SendStreamHeaderResponseH\x00\x12\x43\n\x11send_stream_chunk\x18, \x01(\x0b\x32&.livekit.proto.SendStreamChunkResponseH\x00\x12G\n\x13send_stream_trailer\x18- \x01(\x0b\x32(.livekit.proto.SendStreamTrailerResponseH\x00\x12y\n.set_data_channel_buffered_amount_low_threshold\x18. \x01(\x0b\x32?.livekit.proto.SetDataChannelBufferedAmountLowThresholdResponseH\x00\x12P\n\x18load_audio_filter_plugin\x18\x30 \x01(\x0b\x32,.livekit.proto.LoadAudioFilterPluginResponseH\x00\x12\x30\n\x07new_apm\x18\x31 \x01(\x0b\x32\x1d.livekit.proto.NewApmResponseH\x00\x12\x45\n\x12\x61pm_process_stream\x18\x32 \x01(\x0b\x32\'.livekit.proto.ApmProcessStreamResponseH\x00\x12T\n\x1a\x61pm_process_reverse_stream\x18\x33 \x01(\x0b\x32..livekit.proto.ApmProcessReverseStreamResponseH\x00\x12H\n\x14\x61pm_set_stream_delay\x18\x34 \x01(\x0b\x32(.livekit.proto.ApmSetStreamDelayResponseH\x00\x12W\n\x15\x62yte_read_incremental\x18\x35 \x01(\x0b\x32\x36.livekit.proto.ByteStreamReaderReadIncrementalResponseH\x00\x12G\n\rbyte_read_all\x18\x36 \x01(\x0b\x32..livekit.proto.ByteStreamReaderReadAllResponseH\x00\x12P\n\x12\x62yte_write_to_file\x18\x37 \x01(\x0b\x32\x32.livekit.proto.ByteStreamReaderWriteToFileResponseH\x00\x12W\n\x15text_read_incremental\x18\x38 \x01(\x0b\x32\x36.livekit.proto.TextStreamReaderReadIncrementalResponseH\x00\x12G\n\rtext_read_all\x18\x39 \x01(\x0b\x32..livekit.proto.TextStreamReaderReadAllResponseH\x00\x12:\n\tsend_file\x18: \x01(\x0b\x32%.livekit.proto.StreamSendFileResponseH\x00\x12:\n\tsend_text\x18; \x01(\x0b\x32%.livekit.proto.StreamSendTextResponseH\x00\x12\x41\n\x10\x62yte_stream_open\x18< \x01(\x0b\x32%.livekit.proto.ByteStreamOpenResponseH\x00\x12I\n\x11\x62yte_stream_write\x18= \x01(\x0b\x32,.livekit.proto.ByteStreamWriterWriteResponseH\x00\x12I\n\x11\x62yte_stream_close\x18> \x01(\x0b\x32,.livekit.proto.ByteStreamWriterCloseResponseH\x00\x12\x41\n\x10text_stream_open\x18? \x01(\x0b\x32%.livekit.proto.TextStreamOpenResponseH\x00\x12I\n\x11text_stream_write\x18@ \x01(\x0b\x32,.livekit.proto.TextStreamWriterWriteResponseH\x00\x12I\n\x11text_stream_close\x18\x41 \x01(\x0b\x32,.livekit.proto.TextStreamWriterCloseResponseH\x00\x12<\n\nsend_bytes\x18\x42 \x01(\x0b\x32&.livekit.proto.StreamSendBytesResponseH\x00\x12g\n$set_remote_track_publication_quality\x18\x43 \x01(\x0b\x32\x37.livekit.proto.SetRemoteTrackPublicationQualityResponseH\x00\x12\x45\n\x12publish_data_track\x18\x44 \x01(\x0b\x32\'.livekit.proto.PublishDataTrackResponseH\x00\x12Q\n\x19local_data_track_try_push\x18\x45 \x01(\x0b\x32,.livekit.proto.LocalDataTrackTryPushResponseH\x00\x12T\n\x1alocal_data_track_unpublish\x18\x46 \x01(\x0b\x32..livekit.proto.LocalDataTrackUnpublishResponseH\x00\x12Y\n\x1dlocal_data_track_is_published\x18G \x01(\x0b\x32\x30.livekit.proto.LocalDataTrackIsPublishedResponseH\x00\x12I\n\x14subscribe_data_track\x18H \x01(\x0b\x32).livekit.proto.SubscribeDataTrackResponseH\x00\x12[\n\x1eremote_data_track_is_published\x18I \x01(\x0b\x32\x31.livekit.proto.RemoteDataTrackIsPublishedResponseH\x00\x12L\n\x16\x64\x61ta_track_stream_read\x18J \x01(\x0b\x32*.livekit.proto.DataTrackStreamReadResponseH\x00\x12j\n&remote_data_track_set_pipeline_options\x18T \x01(\x0b\x32\x38.livekit.proto.RemoteDataTrackSetPipelineOptionsResponseH\x00\x12<\n\rdefine_schema\x18U \x01(\x0b\x32#.livekit.proto.DefineSchemaResponseH\x00\x12\x36\n\nget_schema\x18V \x01(\x0b\x32 .livekit.proto.GetSchemaResponseH\x00\x12\x44\n\x11simulate_scenario\x18K \x01(\x0b\x32\'.livekit.proto.SimulateScenarioResponseH\x00\x12\x45\n\x12new_platform_audio\x18L \x01(\x0b\x32\'.livekit.proto.NewPlatformAudioResponseH\x00\x12\x43\n\x11get_audio_devices\x18M \x01(\x0b\x32&.livekit.proto.GetAudioDevicesResponseH\x00\x12I\n\x14set_recording_device\x18N \x01(\x0b\x32).livekit.proto.SetRecordingDeviceResponseH\x00\x12\x45\n\x12set_playout_device\x18O \x01(\x0b\x32\'.livekit.proto.SetPlayoutDeviceResponseH\x00\x12@\n\x0fstart_recording\x18P \x01(\x0b\x32%.livekit.proto.StartRecordingResponseH\x00\x12>\n\x0estop_recording\x18Q \x01(\x0b\x32$.livekit.proto.StopRecordingResponseH\x00\x12H\n\x14ready_for_room_event\x18R \x01(\x0b\x32(.livekit.proto.ReadyForRoomEventResponseH\x00\x42\t\n\x07message\"\xd0\x17\n\x08\x46\x66iEvent\x12.\n\nroom_event\x18\x01 \x01(\x0b\x32\x18.livekit.proto.RoomEventH\x00\x12\x30\n\x0btrack_event\x18\x02 \x01(\x0b\x32\x19.livekit.proto.TrackEventH\x00\x12=\n\x12video_stream_event\x18\x03 \x01(\x0b\x32\x1f.livekit.proto.VideoStreamEventH\x00\x12=\n\x12\x61udio_stream_event\x18\x04 \x01(\x0b\x32\x1f.livekit.proto.AudioStreamEventH\x00\x12\x31\n\x07\x63onnect\x18\x05 \x01(\x0b\x32\x1e.livekit.proto.ConnectCallbackH\x00\x12\x37\n\ndisconnect\x18\x07 \x01(\x0b\x32!.livekit.proto.DisconnectCallbackH\x00\x12\x31\n\x07\x64ispose\x18\x08 \x01(\x0b\x32\x1e.livekit.proto.DisposeCallbackH\x00\x12<\n\rpublish_track\x18\t \x01(\x0b\x32#.livekit.proto.PublishTrackCallbackH\x00\x12@\n\x0funpublish_track\x18\n \x01(\x0b\x32%.livekit.proto.UnpublishTrackCallbackH\x00\x12:\n\x0cpublish_data\x18\x0b \x01(\x0b\x32\".livekit.proto.PublishDataCallbackH\x00\x12L\n\x15publish_transcription\x18\x0c \x01(\x0b\x32+.livekit.proto.PublishTranscriptionCallbackH\x00\x12G\n\x13\x63\x61pture_audio_frame\x18\r \x01(\x0b\x32(.livekit.proto.CaptureAudioFrameCallbackH\x00\x12\x45\n\x12set_local_metadata\x18\x0e \x01(\x0b\x32\'.livekit.proto.SetLocalMetadataCallbackH\x00\x12=\n\x0eset_local_name\x18\x0f \x01(\x0b\x32#.livekit.proto.SetLocalNameCallbackH\x00\x12I\n\x14set_local_attributes\x18\x10 \x01(\x0b\x32).livekit.proto.SetLocalAttributesCallbackH\x00\x12\x34\n\tget_stats\x18\x11 \x01(\x0b\x32\x1f.livekit.proto.GetStatsCallbackH\x00\x12\'\n\x04logs\x18\x12 \x01(\x0b\x32\x17.livekit.proto.LogBatchH\x00\x12\x43\n\x11get_session_stats\x18\x13 \x01(\x0b\x32&.livekit.proto.GetSessionStatsCallbackH\x00\x12%\n\x05panic\x18\x14 \x01(\x0b\x32\x14.livekit.proto.PanicH\x00\x12\x41\n\x10publish_sip_dtmf\x18\x15 \x01(\x0b\x32%.livekit.proto.PublishSipDtmfCallbackH\x00\x12>\n\x0c\x63hat_message\x18\x16 \x01(\x0b\x32&.livekit.proto.SendChatMessageCallbackH\x00\x12\x38\n\x0bperform_rpc\x18\x17 \x01(\x0b\x32!.livekit.proto.PerformRpcCallbackH\x00\x12H\n\x15rpc_method_invocation\x18\x18 \x01(\x0b\x32\'.livekit.proto.RpcMethodInvocationEventH\x00\x12\x45\n\x12send_stream_header\x18\x19 \x01(\x0b\x32\'.livekit.proto.SendStreamHeaderCallbackH\x00\x12\x43\n\x11send_stream_chunk\x18\x1a \x01(\x0b\x32&.livekit.proto.SendStreamChunkCallbackH\x00\x12G\n\x13send_stream_trailer\x18\x1b \x01(\x0b\x32(.livekit.proto.SendStreamTrailerCallbackH\x00\x12H\n\x18\x62yte_stream_reader_event\x18\x1c \x01(\x0b\x32$.livekit.proto.ByteStreamReaderEventH\x00\x12U\n\x1b\x62yte_stream_reader_read_all\x18\x1d \x01(\x0b\x32..livekit.proto.ByteStreamReaderReadAllCallbackH\x00\x12^\n byte_stream_reader_write_to_file\x18\x1e \x01(\x0b\x32\x32.livekit.proto.ByteStreamReaderWriteToFileCallbackH\x00\x12\x41\n\x10\x62yte_stream_open\x18\x1f \x01(\x0b\x32%.livekit.proto.ByteStreamOpenCallbackH\x00\x12P\n\x18\x62yte_stream_writer_write\x18 \x01(\x0b\x32,.livekit.proto.ByteStreamWriterWriteCallbackH\x00\x12P\n\x18\x62yte_stream_writer_close\x18! \x01(\x0b\x32,.livekit.proto.ByteStreamWriterCloseCallbackH\x00\x12:\n\tsend_file\x18\" \x01(\x0b\x32%.livekit.proto.StreamSendFileCallbackH\x00\x12H\n\x18text_stream_reader_event\x18# \x01(\x0b\x32$.livekit.proto.TextStreamReaderEventH\x00\x12U\n\x1btext_stream_reader_read_all\x18$ \x01(\x0b\x32..livekit.proto.TextStreamReaderReadAllCallbackH\x00\x12\x41\n\x10text_stream_open\x18% \x01(\x0b\x32%.livekit.proto.TextStreamOpenCallbackH\x00\x12P\n\x18text_stream_writer_write\x18& \x01(\x0b\x32,.livekit.proto.TextStreamWriterWriteCallbackH\x00\x12P\n\x18text_stream_writer_close\x18\' \x01(\x0b\x32,.livekit.proto.TextStreamWriterCloseCallbackH\x00\x12:\n\tsend_text\x18( \x01(\x0b\x32%.livekit.proto.StreamSendTextCallbackH\x00\x12<\n\nsend_bytes\x18) \x01(\x0b\x32&.livekit.proto.StreamSendBytesCallbackH\x00\x12\x45\n\x12publish_data_track\x18* \x01(\x0b\x32\'.livekit.proto.PublishDataTrackCallbackH\x00\x12\x46\n\x17\x64\x61ta_track_stream_event\x18+ \x01(\x0b\x32#.livekit.proto.DataTrackStreamEventH\x00\x12\x44\n\x11simulate_scenario\x18, \x01(\x0b\x32\'.livekit.proto.SimulateScenarioCallbackH\x00\x12<\n\rdefine_schema\x18- \x01(\x0b\x32#.livekit.proto.DefineSchemaCallbackH\x00\x12\x36\n\nget_schema\x18. \x01(\x0b\x32 .livekit.proto.GetSchemaCallbackH\x00\x42\t\n\x07message\"\x1f\n\x0e\x44isposeRequest\x12\r\n\x05\x61sync\x18\x01 \x02(\x08\"#\n\x0f\x44isposeResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"#\n\x0f\x44isposeCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x85\x01\n\tLogRecord\x12&\n\x05level\x18\x01 \x02(\x0e\x32\x17.livekit.proto.LogLevel\x12\x0e\n\x06target\x18\x02 \x02(\t\x12\x13\n\x0bmodule_path\x18\x03 \x01(\t\x12\x0c\n\x04\x66ile\x18\x04 \x01(\t\x12\x0c\n\x04line\x18\x05 \x01(\r\x12\x0f\n\x07message\x18\x06 \x02(\t\"5\n\x08LogBatch\x12)\n\x07records\x18\x01 \x03(\x0b\x32\x18.livekit.proto.LogRecord\"\x18\n\x05Panic\x12\x0f\n\x07message\x18\x01 \x02(\t*S\n\x08LogLevel\x12\r\n\tLOG_ERROR\x10\x00\x12\x0c\n\x08LOG_WARN\x10\x01\x12\x0c\n\x08LOG_INFO\x10\x02\x12\r\n\tLOG_DEBUG\x10\x03\x12\r\n\tLOG_TRACE\x10\x04\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -27,24 +31,24 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_LOGLEVEL']._serialized_start=4748 - _globals['_LOGLEVEL']._serialized_end=4831 - _globals['_FFIREQUEST']._serialized_start=104 - _globals['_FFIREQUEST']._serialized_end=1668 - _globals['_FFIRESPONSE']._serialized_start=1671 - _globals['_FFIRESPONSE']._serialized_end=3260 - _globals['_FFIEVENT']._serialized_start=3263 - _globals['_FFIEVENT']._serialized_end=4355 - _globals['_DISPOSEREQUEST']._serialized_start=4357 - _globals['_DISPOSEREQUEST']._serialized_end=4388 - _globals['_DISPOSERESPONSE']._serialized_start=4390 - _globals['_DISPOSERESPONSE']._serialized_end=4443 - _globals['_DISPOSECALLBACK']._serialized_start=4445 - _globals['_DISPOSECALLBACK']._serialized_end=4480 - _globals['_LOGRECORD']._serialized_start=4483 - _globals['_LOGRECORD']._serialized_end=4665 - _globals['_LOGBATCH']._serialized_start=4667 - _globals['_LOGBATCH']._serialized_end=4720 - _globals['_PANIC']._serialized_start=4722 - _globals['_PANIC']._serialized_end=4746 + _globals['_LOGLEVEL']._serialized_start=15916 + _globals['_LOGLEVEL']._serialized_end=15999 + _globals['_FFIREQUEST']._serialized_start=177 + _globals['_FFIREQUEST']._serialized_end=6360 + _globals['_FFIRESPONSE']._serialized_start=6363 + _globals['_FFIRESPONSE']._serialized_end=12563 + _globals['_FFIEVENT']._serialized_start=12566 + _globals['_FFIEVENT']._serialized_end=15590 + _globals['_DISPOSEREQUEST']._serialized_start=15592 + _globals['_DISPOSEREQUEST']._serialized_end=15623 + _globals['_DISPOSERESPONSE']._serialized_start=15625 + _globals['_DISPOSERESPONSE']._serialized_end=15660 + _globals['_DISPOSECALLBACK']._serialized_start=15662 + _globals['_DISPOSECALLBACK']._serialized_end=15697 + _globals['_LOGRECORD']._serialized_start=15700 + _globals['_LOGRECORD']._serialized_end=15833 + _globals['_LOGBATCH']._serialized_start=15835 + _globals['_LOGBATCH']._serialized_end=15888 + _globals['_PANIC']._serialized_start=15890 + _globals['_PANIC']._serialized_end=15914 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/ffi_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/ffi_pb2.pyi index 259880ab..14fdd46c 100644 --- a/livekit-rtc/livekit/rtc/_proto/ffi_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/ffi_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,17 +15,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + from . import audio_frame_pb2 import builtins import collections.abc +from . import data_stream_pb2 +from . import data_track_pb2 from . import e2ee_pb2 import google.protobuf.descriptor import google.protobuf.internal.containers import google.protobuf.internal.enum_type_wrapper import google.protobuf.message from . import room_pb2 +from . import rpc_pb2 import sys from . import track_pb2 +from . import track_publication_pb2 import typing from . import video_frame_pb2 @@ -57,7 +62,7 @@ LOG_DEBUG: LogLevel.ValueType # 3 LOG_TRACE: LogLevel.ValueType # 4 global___LogLevel = LogLevel -@typing_extensions.final +@typing.final class FfiRequest(google.protobuf.message.Message): """**How is the livekit-ffi working: We refer as the ffi server the Rust server that is running the LiveKit client implementation, and we @@ -98,28 +103,90 @@ class FfiRequest(google.protobuf.message.Message): UNPUBLISH_TRACK_FIELD_NUMBER: builtins.int PUBLISH_DATA_FIELD_NUMBER: builtins.int SET_SUBSCRIBED_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_METADATA_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_METADATA_FIELD_NUMBER: builtins.int + SET_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_ATTRIBUTES_FIELD_NUMBER: builtins.int GET_SESSION_STATS_FIELD_NUMBER: builtins.int PUBLISH_TRANSCRIPTION_FIELD_NUMBER: builtins.int + PUBLISH_SIP_DTMF_FIELD_NUMBER: builtins.int CREATE_VIDEO_TRACK_FIELD_NUMBER: builtins.int CREATE_AUDIO_TRACK_FIELD_NUMBER: builtins.int + LOCAL_TRACK_MUTE_FIELD_NUMBER: builtins.int + ENABLE_REMOTE_TRACK_FIELD_NUMBER: builtins.int GET_STATS_FIELD_NUMBER: builtins.int + SET_TRACK_SUBSCRIPTION_PERMISSIONS_FIELD_NUMBER: builtins.int NEW_VIDEO_STREAM_FIELD_NUMBER: builtins.int NEW_VIDEO_SOURCE_FIELD_NUMBER: builtins.int CAPTURE_VIDEO_FRAME_FIELD_NUMBER: builtins.int VIDEO_CONVERT_FIELD_NUMBER: builtins.int + VIDEO_STREAM_FROM_PARTICIPANT_FIELD_NUMBER: builtins.int NEW_AUDIO_STREAM_FIELD_NUMBER: builtins.int NEW_AUDIO_SOURCE_FIELD_NUMBER: builtins.int CAPTURE_AUDIO_FRAME_FIELD_NUMBER: builtins.int + CLEAR_AUDIO_BUFFER_FIELD_NUMBER: builtins.int NEW_AUDIO_RESAMPLER_FIELD_NUMBER: builtins.int REMIX_AND_RESAMPLE_FIELD_NUMBER: builtins.int E2EE_FIELD_NUMBER: builtins.int + AUDIO_STREAM_FROM_PARTICIPANT_FIELD_NUMBER: builtins.int + NEW_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + PUSH_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + FLUSH_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + SEND_CHAT_MESSAGE_FIELD_NUMBER: builtins.int + EDIT_CHAT_MESSAGE_FIELD_NUMBER: builtins.int + PERFORM_RPC_FIELD_NUMBER: builtins.int + REGISTER_RPC_METHOD_FIELD_NUMBER: builtins.int + UNREGISTER_RPC_METHOD_FIELD_NUMBER: builtins.int + RPC_METHOD_INVOCATION_RESPONSE_FIELD_NUMBER: builtins.int + ENABLE_REMOTE_TRACK_PUBLICATION_FIELD_NUMBER: builtins.int + UPDATE_REMOTE_TRACK_PUBLICATION_DIMENSION_FIELD_NUMBER: builtins.int + SEND_STREAM_HEADER_FIELD_NUMBER: builtins.int + SEND_STREAM_CHUNK_FIELD_NUMBER: builtins.int + SEND_STREAM_TRAILER_FIELD_NUMBER: builtins.int + SET_DATA_CHANNEL_BUFFERED_AMOUNT_LOW_THRESHOLD_FIELD_NUMBER: builtins.int + LOAD_AUDIO_FILTER_PLUGIN_FIELD_NUMBER: builtins.int + NEW_APM_FIELD_NUMBER: builtins.int + APM_PROCESS_STREAM_FIELD_NUMBER: builtins.int + APM_PROCESS_REVERSE_STREAM_FIELD_NUMBER: builtins.int + APM_SET_STREAM_DELAY_FIELD_NUMBER: builtins.int + BYTE_READ_INCREMENTAL_FIELD_NUMBER: builtins.int + BYTE_READ_ALL_FIELD_NUMBER: builtins.int + BYTE_WRITE_TO_FILE_FIELD_NUMBER: builtins.int + TEXT_READ_INCREMENTAL_FIELD_NUMBER: builtins.int + TEXT_READ_ALL_FIELD_NUMBER: builtins.int + SEND_FILE_FIELD_NUMBER: builtins.int + SEND_TEXT_FIELD_NUMBER: builtins.int + BYTE_STREAM_OPEN_FIELD_NUMBER: builtins.int + BYTE_STREAM_WRITE_FIELD_NUMBER: builtins.int + BYTE_STREAM_CLOSE_FIELD_NUMBER: builtins.int + TEXT_STREAM_OPEN_FIELD_NUMBER: builtins.int + TEXT_STREAM_WRITE_FIELD_NUMBER: builtins.int + TEXT_STREAM_CLOSE_FIELD_NUMBER: builtins.int + SEND_BYTES_FIELD_NUMBER: builtins.int + SET_REMOTE_TRACK_PUBLICATION_QUALITY_FIELD_NUMBER: builtins.int + PUBLISH_DATA_TRACK_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_TRY_PUSH_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_UNPUBLISH_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_IS_PUBLISHED_FIELD_NUMBER: builtins.int + SUBSCRIBE_DATA_TRACK_FIELD_NUMBER: builtins.int + REMOTE_DATA_TRACK_IS_PUBLISHED_FIELD_NUMBER: builtins.int + DATA_TRACK_STREAM_READ_FIELD_NUMBER: builtins.int + REMOTE_DATA_TRACK_SET_PIPELINE_OPTIONS_FIELD_NUMBER: builtins.int + DEFINE_SCHEMA_FIELD_NUMBER: builtins.int + GET_SCHEMA_FIELD_NUMBER: builtins.int + SIMULATE_SCENARIO_FIELD_NUMBER: builtins.int + NEW_PLATFORM_AUDIO_FIELD_NUMBER: builtins.int + GET_AUDIO_DEVICES_FIELD_NUMBER: builtins.int + SET_RECORDING_DEVICE_FIELD_NUMBER: builtins.int + SET_PLAYOUT_DEVICE_FIELD_NUMBER: builtins.int + START_RECORDING_FIELD_NUMBER: builtins.int + STOP_RECORDING_FIELD_NUMBER: builtins.int + READY_FOR_ROOM_EVENT_FIELD_NUMBER: builtins.int @property def dispose(self) -> global___DisposeRequest: ... @property def connect(self) -> room_pb2.ConnectRequest: """Room""" + @property def disconnect(self) -> room_pb2.DisconnectRequest: ... @property @@ -131,23 +198,35 @@ class FfiRequest(google.protobuf.message.Message): @property def set_subscribed(self) -> room_pb2.SetSubscribedRequest: ... @property - def update_local_metadata(self) -> room_pb2.UpdateLocalMetadataRequest: ... + def set_local_metadata(self) -> room_pb2.SetLocalMetadataRequest: ... + @property + def set_local_name(self) -> room_pb2.SetLocalNameRequest: ... @property - def update_local_name(self) -> room_pb2.UpdateLocalNameRequest: ... + def set_local_attributes(self) -> room_pb2.SetLocalAttributesRequest: ... @property def get_session_stats(self) -> room_pb2.GetSessionStatsRequest: ... @property def publish_transcription(self) -> room_pb2.PublishTranscriptionRequest: ... @property + def publish_sip_dtmf(self) -> room_pb2.PublishSipDtmfRequest: ... + @property def create_video_track(self) -> track_pb2.CreateVideoTrackRequest: """Track""" + @property def create_audio_track(self) -> track_pb2.CreateAudioTrackRequest: ... @property + def local_track_mute(self) -> track_pb2.LocalTrackMuteRequest: ... + @property + def enable_remote_track(self) -> track_pb2.EnableRemoteTrackRequest: ... + @property def get_stats(self) -> track_pb2.GetStatsRequest: ... @property + def set_track_subscription_permissions(self) -> track_pb2.SetTrackSubscriptionPermissionsRequest: ... + @property def new_video_stream(self) -> video_frame_pb2.NewVideoStreamRequest: """Video""" + @property def new_video_source(self) -> video_frame_pb2.NewVideoSourceRequest: ... @property @@ -155,18 +234,155 @@ class FfiRequest(google.protobuf.message.Message): @property def video_convert(self) -> video_frame_pb2.VideoConvertRequest: ... @property + def video_stream_from_participant(self) -> video_frame_pb2.VideoStreamFromParticipantRequest: ... + @property def new_audio_stream(self) -> audio_frame_pb2.NewAudioStreamRequest: """Audio""" + @property def new_audio_source(self) -> audio_frame_pb2.NewAudioSourceRequest: ... @property def capture_audio_frame(self) -> audio_frame_pb2.CaptureAudioFrameRequest: ... @property + def clear_audio_buffer(self) -> audio_frame_pb2.ClearAudioBufferRequest: ... + @property def new_audio_resampler(self) -> audio_frame_pb2.NewAudioResamplerRequest: ... @property def remix_and_resample(self) -> audio_frame_pb2.RemixAndResampleRequest: ... @property def e2ee(self) -> e2ee_pb2.E2eeRequest: ... + @property + def audio_stream_from_participant(self) -> audio_frame_pb2.AudioStreamFromParticipantRequest: ... + @property + def new_sox_resampler(self) -> audio_frame_pb2.NewSoxResamplerRequest: ... + @property + def push_sox_resampler(self) -> audio_frame_pb2.PushSoxResamplerRequest: ... + @property + def flush_sox_resampler(self) -> audio_frame_pb2.FlushSoxResamplerRequest: ... + @property + def send_chat_message(self) -> room_pb2.SendChatMessageRequest: ... + @property + def edit_chat_message(self) -> room_pb2.EditChatMessageRequest: ... + @property + def perform_rpc(self) -> rpc_pb2.PerformRpcRequest: + """RPC""" + + @property + def register_rpc_method(self) -> rpc_pb2.RegisterRpcMethodRequest: ... + @property + def unregister_rpc_method(self) -> rpc_pb2.UnregisterRpcMethodRequest: ... + @property + def rpc_method_invocation_response(self) -> rpc_pb2.RpcMethodInvocationResponseRequest: ... + @property + def enable_remote_track_publication(self) -> track_publication_pb2.EnableRemoteTrackPublicationRequest: + """Track Publication""" + + @property + def update_remote_track_publication_dimension(self) -> track_publication_pb2.UpdateRemoteTrackPublicationDimensionRequest: ... + @property + def send_stream_header(self) -> room_pb2.SendStreamHeaderRequest: + """Data Streams (low level)""" + + @property + def send_stream_chunk(self) -> room_pb2.SendStreamChunkRequest: ... + @property + def send_stream_trailer(self) -> room_pb2.SendStreamTrailerRequest: ... + @property + def set_data_channel_buffered_amount_low_threshold(self) -> room_pb2.SetDataChannelBufferedAmountLowThresholdRequest: + """Data Channel""" + + @property + def load_audio_filter_plugin(self) -> audio_frame_pb2.LoadAudioFilterPluginRequest: + """Audio Filter Plugin""" + + @property + def new_apm(self) -> audio_frame_pb2.NewApmRequest: ... + @property + def apm_process_stream(self) -> audio_frame_pb2.ApmProcessStreamRequest: ... + @property + def apm_process_reverse_stream(self) -> audio_frame_pb2.ApmProcessReverseStreamRequest: ... + @property + def apm_set_stream_delay(self) -> audio_frame_pb2.ApmSetStreamDelayRequest: ... + @property + def byte_read_incremental(self) -> data_stream_pb2.ByteStreamReaderReadIncrementalRequest: + """Data Streams (high level)""" + + @property + def byte_read_all(self) -> data_stream_pb2.ByteStreamReaderReadAllRequest: ... + @property + def byte_write_to_file(self) -> data_stream_pb2.ByteStreamReaderWriteToFileRequest: ... + @property + def text_read_incremental(self) -> data_stream_pb2.TextStreamReaderReadIncrementalRequest: ... + @property + def text_read_all(self) -> data_stream_pb2.TextStreamReaderReadAllRequest: ... + @property + def send_file(self) -> data_stream_pb2.StreamSendFileRequest: ... + @property + def send_text(self) -> data_stream_pb2.StreamSendTextRequest: ... + @property + def byte_stream_open(self) -> data_stream_pb2.ByteStreamOpenRequest: ... + @property + def byte_stream_write(self) -> data_stream_pb2.ByteStreamWriterWriteRequest: ... + @property + def byte_stream_close(self) -> data_stream_pb2.ByteStreamWriterCloseRequest: ... + @property + def text_stream_open(self) -> data_stream_pb2.TextStreamOpenRequest: ... + @property + def text_stream_write(self) -> data_stream_pb2.TextStreamWriterWriteRequest: ... + @property + def text_stream_close(self) -> data_stream_pb2.TextStreamWriterCloseRequest: ... + @property + def send_bytes(self) -> data_stream_pb2.StreamSendBytesRequest: ... + @property + def set_remote_track_publication_quality(self) -> track_publication_pb2.SetRemoteTrackPublicationQualityRequest: ... + @property + def publish_data_track(self) -> data_track_pb2.PublishDataTrackRequest: + """Data Track (local)""" + + @property + def local_data_track_try_push(self) -> data_track_pb2.LocalDataTrackTryPushRequest: ... + @property + def local_data_track_unpublish(self) -> data_track_pb2.LocalDataTrackUnpublishRequest: ... + @property + def local_data_track_is_published(self) -> data_track_pb2.LocalDataTrackIsPublishedRequest: ... + @property + def subscribe_data_track(self) -> data_track_pb2.SubscribeDataTrackRequest: + """Data Track (remote)""" + + @property + def remote_data_track_is_published(self) -> data_track_pb2.RemoteDataTrackIsPublishedRequest: ... + @property + def data_track_stream_read(self) -> data_track_pb2.DataTrackStreamReadRequest: ... + @property + def remote_data_track_set_pipeline_options(self) -> data_track_pb2.RemoteDataTrackSetPipelineOptionsRequest: ... + @property + def define_schema(self) -> data_track_pb2.DefineSchemaRequest: + """Data Track (schemas)""" + + @property + def get_schema(self) -> data_track_pb2.GetSchemaRequest: ... + @property + def simulate_scenario(self) -> room_pb2.SimulateScenarioRequest: + """Reconnection / chaos testing""" + + @property + def new_platform_audio(self) -> audio_frame_pb2.NewPlatformAudioRequest: + """Platform Audio (ADM)""" + + @property + def get_audio_devices(self) -> audio_frame_pb2.GetAudioDevicesRequest: ... + @property + def set_recording_device(self) -> audio_frame_pb2.SetRecordingDeviceRequest: ... + @property + def set_playout_device(self) -> audio_frame_pb2.SetPlayoutDeviceRequest: ... + @property + def start_recording(self) -> audio_frame_pb2.StartRecordingRequest: ... + @property + def stop_recording(self) -> audio_frame_pb2.StopRecordingRequest: ... + @property + def ready_for_room_event(self) -> room_pb2.ReadyForRoomEventRequest: + """Room event ready signal""" + def __init__( self, *, @@ -177,31 +393,92 @@ class FfiRequest(google.protobuf.message.Message): unpublish_track: room_pb2.UnpublishTrackRequest | None = ..., publish_data: room_pb2.PublishDataRequest | None = ..., set_subscribed: room_pb2.SetSubscribedRequest | None = ..., - update_local_metadata: room_pb2.UpdateLocalMetadataRequest | None = ..., - update_local_name: room_pb2.UpdateLocalNameRequest | None = ..., + set_local_metadata: room_pb2.SetLocalMetadataRequest | None = ..., + set_local_name: room_pb2.SetLocalNameRequest | None = ..., + set_local_attributes: room_pb2.SetLocalAttributesRequest | None = ..., get_session_stats: room_pb2.GetSessionStatsRequest | None = ..., publish_transcription: room_pb2.PublishTranscriptionRequest | None = ..., + publish_sip_dtmf: room_pb2.PublishSipDtmfRequest | None = ..., create_video_track: track_pb2.CreateVideoTrackRequest | None = ..., create_audio_track: track_pb2.CreateAudioTrackRequest | None = ..., + local_track_mute: track_pb2.LocalTrackMuteRequest | None = ..., + enable_remote_track: track_pb2.EnableRemoteTrackRequest | None = ..., get_stats: track_pb2.GetStatsRequest | None = ..., + set_track_subscription_permissions: track_pb2.SetTrackSubscriptionPermissionsRequest | None = ..., new_video_stream: video_frame_pb2.NewVideoStreamRequest | None = ..., new_video_source: video_frame_pb2.NewVideoSourceRequest | None = ..., capture_video_frame: video_frame_pb2.CaptureVideoFrameRequest | None = ..., video_convert: video_frame_pb2.VideoConvertRequest | None = ..., + video_stream_from_participant: video_frame_pb2.VideoStreamFromParticipantRequest | None = ..., new_audio_stream: audio_frame_pb2.NewAudioStreamRequest | None = ..., new_audio_source: audio_frame_pb2.NewAudioSourceRequest | None = ..., capture_audio_frame: audio_frame_pb2.CaptureAudioFrameRequest | None = ..., + clear_audio_buffer: audio_frame_pb2.ClearAudioBufferRequest | None = ..., new_audio_resampler: audio_frame_pb2.NewAudioResamplerRequest | None = ..., remix_and_resample: audio_frame_pb2.RemixAndResampleRequest | None = ..., e2ee: e2ee_pb2.E2eeRequest | None = ..., + audio_stream_from_participant: audio_frame_pb2.AudioStreamFromParticipantRequest | None = ..., + new_sox_resampler: audio_frame_pb2.NewSoxResamplerRequest | None = ..., + push_sox_resampler: audio_frame_pb2.PushSoxResamplerRequest | None = ..., + flush_sox_resampler: audio_frame_pb2.FlushSoxResamplerRequest | None = ..., + send_chat_message: room_pb2.SendChatMessageRequest | None = ..., + edit_chat_message: room_pb2.EditChatMessageRequest | None = ..., + perform_rpc: rpc_pb2.PerformRpcRequest | None = ..., + register_rpc_method: rpc_pb2.RegisterRpcMethodRequest | None = ..., + unregister_rpc_method: rpc_pb2.UnregisterRpcMethodRequest | None = ..., + rpc_method_invocation_response: rpc_pb2.RpcMethodInvocationResponseRequest | None = ..., + enable_remote_track_publication: track_publication_pb2.EnableRemoteTrackPublicationRequest | None = ..., + update_remote_track_publication_dimension: track_publication_pb2.UpdateRemoteTrackPublicationDimensionRequest | None = ..., + send_stream_header: room_pb2.SendStreamHeaderRequest | None = ..., + send_stream_chunk: room_pb2.SendStreamChunkRequest | None = ..., + send_stream_trailer: room_pb2.SendStreamTrailerRequest | None = ..., + set_data_channel_buffered_amount_low_threshold: room_pb2.SetDataChannelBufferedAmountLowThresholdRequest | None = ..., + load_audio_filter_plugin: audio_frame_pb2.LoadAudioFilterPluginRequest | None = ..., + new_apm: audio_frame_pb2.NewApmRequest | None = ..., + apm_process_stream: audio_frame_pb2.ApmProcessStreamRequest | None = ..., + apm_process_reverse_stream: audio_frame_pb2.ApmProcessReverseStreamRequest | None = ..., + apm_set_stream_delay: audio_frame_pb2.ApmSetStreamDelayRequest | None = ..., + byte_read_incremental: data_stream_pb2.ByteStreamReaderReadIncrementalRequest | None = ..., + byte_read_all: data_stream_pb2.ByteStreamReaderReadAllRequest | None = ..., + byte_write_to_file: data_stream_pb2.ByteStreamReaderWriteToFileRequest | None = ..., + text_read_incremental: data_stream_pb2.TextStreamReaderReadIncrementalRequest | None = ..., + text_read_all: data_stream_pb2.TextStreamReaderReadAllRequest | None = ..., + send_file: data_stream_pb2.StreamSendFileRequest | None = ..., + send_text: data_stream_pb2.StreamSendTextRequest | None = ..., + byte_stream_open: data_stream_pb2.ByteStreamOpenRequest | None = ..., + byte_stream_write: data_stream_pb2.ByteStreamWriterWriteRequest | None = ..., + byte_stream_close: data_stream_pb2.ByteStreamWriterCloseRequest | None = ..., + text_stream_open: data_stream_pb2.TextStreamOpenRequest | None = ..., + text_stream_write: data_stream_pb2.TextStreamWriterWriteRequest | None = ..., + text_stream_close: data_stream_pb2.TextStreamWriterCloseRequest | None = ..., + send_bytes: data_stream_pb2.StreamSendBytesRequest | None = ..., + set_remote_track_publication_quality: track_publication_pb2.SetRemoteTrackPublicationQualityRequest | None = ..., + publish_data_track: data_track_pb2.PublishDataTrackRequest | None = ..., + local_data_track_try_push: data_track_pb2.LocalDataTrackTryPushRequest | None = ..., + local_data_track_unpublish: data_track_pb2.LocalDataTrackUnpublishRequest | None = ..., + local_data_track_is_published: data_track_pb2.LocalDataTrackIsPublishedRequest | None = ..., + subscribe_data_track: data_track_pb2.SubscribeDataTrackRequest | None = ..., + remote_data_track_is_published: data_track_pb2.RemoteDataTrackIsPublishedRequest | None = ..., + data_track_stream_read: data_track_pb2.DataTrackStreamReadRequest | None = ..., + remote_data_track_set_pipeline_options: data_track_pb2.RemoteDataTrackSetPipelineOptionsRequest | None = ..., + define_schema: data_track_pb2.DefineSchemaRequest | None = ..., + get_schema: data_track_pb2.GetSchemaRequest | None = ..., + simulate_scenario: room_pb2.SimulateScenarioRequest | None = ..., + new_platform_audio: audio_frame_pb2.NewPlatformAudioRequest | None = ..., + get_audio_devices: audio_frame_pb2.GetAudioDevicesRequest | None = ..., + set_recording_device: audio_frame_pb2.SetRecordingDeviceRequest | None = ..., + set_playout_device: audio_frame_pb2.SetPlayoutDeviceRequest | None = ..., + start_recording: audio_frame_pb2.StartRecordingRequest | None = ..., + stop_recording: audio_frame_pb2.StopRecordingRequest | None = ..., + ready_for_room_event: room_pb2.ReadyForRoomEventRequest | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "message", b"message", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "remix_and_resample", b"remix_and_resample", "set_subscribed", b"set_subscribed", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_convert", b"video_convert"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "message", b"message", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "remix_and_resample", b"remix_and_resample", "set_subscribed", b"set_subscribed", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_convert", b"video_convert"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["dispose", "connect", "disconnect", "publish_track", "unpublish_track", "publish_data", "set_subscribed", "update_local_metadata", "update_local_name", "get_session_stats", "publish_transcription", "create_video_track", "create_audio_track", "get_stats", "new_video_stream", "new_video_source", "capture_video_frame", "video_convert", "new_audio_stream", "new_audio_source", "capture_audio_frame", "new_audio_resampler", "remix_and_resample", "e2ee"] | None: ... + def HasField(self, field_name: typing.Literal["apm_process_reverse_stream", b"apm_process_reverse_stream", "apm_process_stream", b"apm_process_stream", "apm_set_stream_delay", b"apm_set_stream_delay", "audio_stream_from_participant", b"audio_stream_from_participant", "byte_read_all", b"byte_read_all", "byte_read_incremental", b"byte_read_incremental", "byte_stream_close", b"byte_stream_close", "byte_stream_open", b"byte_stream_open", "byte_stream_write", b"byte_stream_write", "byte_write_to_file", b"byte_write_to_file", "capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "clear_audio_buffer", b"clear_audio_buffer", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "data_track_stream_read", b"data_track_stream_read", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "edit_chat_message", b"edit_chat_message", "enable_remote_track", b"enable_remote_track", "enable_remote_track_publication", b"enable_remote_track_publication", "flush_sox_resampler", b"flush_sox_resampler", "get_audio_devices", b"get_audio_devices", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "load_audio_filter_plugin", b"load_audio_filter_plugin", "local_data_track_is_published", b"local_data_track_is_published", "local_data_track_try_push", b"local_data_track_try_push", "local_data_track_unpublish", b"local_data_track_unpublish", "local_track_mute", b"local_track_mute", "message", b"message", "new_apm", b"new_apm", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_platform_audio", b"new_platform_audio", "new_sox_resampler", b"new_sox_resampler", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "push_sox_resampler", b"push_sox_resampler", "ready_for_room_event", b"ready_for_room_event", "register_rpc_method", b"register_rpc_method", "remix_and_resample", b"remix_and_resample", "remote_data_track_is_published", b"remote_data_track_is_published", "remote_data_track_set_pipeline_options", b"remote_data_track_set_pipeline_options", "rpc_method_invocation_response", b"rpc_method_invocation_response", "send_bytes", b"send_bytes", "send_chat_message", b"send_chat_message", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_data_channel_buffered_amount_low_threshold", b"set_data_channel_buffered_amount_low_threshold", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "set_playout_device", b"set_playout_device", "set_recording_device", b"set_recording_device", "set_remote_track_publication_quality", b"set_remote_track_publication_quality", "set_subscribed", b"set_subscribed", "set_track_subscription_permissions", b"set_track_subscription_permissions", "simulate_scenario", b"simulate_scenario", "start_recording", b"start_recording", "stop_recording", b"stop_recording", "subscribe_data_track", b"subscribe_data_track", "text_read_all", b"text_read_all", "text_read_incremental", b"text_read_incremental", "text_stream_close", b"text_stream_close", "text_stream_open", b"text_stream_open", "text_stream_write", b"text_stream_write", "unpublish_track", b"unpublish_track", "unregister_rpc_method", b"unregister_rpc_method", "update_remote_track_publication_dimension", b"update_remote_track_publication_dimension", "video_convert", b"video_convert", "video_stream_from_participant", b"video_stream_from_participant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm_process_reverse_stream", b"apm_process_reverse_stream", "apm_process_stream", b"apm_process_stream", "apm_set_stream_delay", b"apm_set_stream_delay", "audio_stream_from_participant", b"audio_stream_from_participant", "byte_read_all", b"byte_read_all", "byte_read_incremental", b"byte_read_incremental", "byte_stream_close", b"byte_stream_close", "byte_stream_open", b"byte_stream_open", "byte_stream_write", b"byte_stream_write", "byte_write_to_file", b"byte_write_to_file", "capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "clear_audio_buffer", b"clear_audio_buffer", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "data_track_stream_read", b"data_track_stream_read", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "edit_chat_message", b"edit_chat_message", "enable_remote_track", b"enable_remote_track", "enable_remote_track_publication", b"enable_remote_track_publication", "flush_sox_resampler", b"flush_sox_resampler", "get_audio_devices", b"get_audio_devices", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "load_audio_filter_plugin", b"load_audio_filter_plugin", "local_data_track_is_published", b"local_data_track_is_published", "local_data_track_try_push", b"local_data_track_try_push", "local_data_track_unpublish", b"local_data_track_unpublish", "local_track_mute", b"local_track_mute", "message", b"message", "new_apm", b"new_apm", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_platform_audio", b"new_platform_audio", "new_sox_resampler", b"new_sox_resampler", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "push_sox_resampler", b"push_sox_resampler", "ready_for_room_event", b"ready_for_room_event", "register_rpc_method", b"register_rpc_method", "remix_and_resample", b"remix_and_resample", "remote_data_track_is_published", b"remote_data_track_is_published", "remote_data_track_set_pipeline_options", b"remote_data_track_set_pipeline_options", "rpc_method_invocation_response", b"rpc_method_invocation_response", "send_bytes", b"send_bytes", "send_chat_message", b"send_chat_message", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_data_channel_buffered_amount_low_threshold", b"set_data_channel_buffered_amount_low_threshold", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "set_playout_device", b"set_playout_device", "set_recording_device", b"set_recording_device", "set_remote_track_publication_quality", b"set_remote_track_publication_quality", "set_subscribed", b"set_subscribed", "set_track_subscription_permissions", b"set_track_subscription_permissions", "simulate_scenario", b"simulate_scenario", "start_recording", b"start_recording", "stop_recording", b"stop_recording", "subscribe_data_track", b"subscribe_data_track", "text_read_all", b"text_read_all", "text_read_incremental", b"text_read_incremental", "text_stream_close", b"text_stream_close", "text_stream_open", b"text_stream_open", "text_stream_write", b"text_stream_write", "unpublish_track", b"unpublish_track", "unregister_rpc_method", b"unregister_rpc_method", "update_remote_track_publication_dimension", b"update_remote_track_publication_dimension", "video_convert", b"video_convert", "video_stream_from_participant", b"video_stream_from_participant"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["dispose", "connect", "disconnect", "publish_track", "unpublish_track", "publish_data", "set_subscribed", "set_local_metadata", "set_local_name", "set_local_attributes", "get_session_stats", "publish_transcription", "publish_sip_dtmf", "create_video_track", "create_audio_track", "local_track_mute", "enable_remote_track", "get_stats", "set_track_subscription_permissions", "new_video_stream", "new_video_source", "capture_video_frame", "video_convert", "video_stream_from_participant", "new_audio_stream", "new_audio_source", "capture_audio_frame", "clear_audio_buffer", "new_audio_resampler", "remix_and_resample", "e2ee", "audio_stream_from_participant", "new_sox_resampler", "push_sox_resampler", "flush_sox_resampler", "send_chat_message", "edit_chat_message", "perform_rpc", "register_rpc_method", "unregister_rpc_method", "rpc_method_invocation_response", "enable_remote_track_publication", "update_remote_track_publication_dimension", "send_stream_header", "send_stream_chunk", "send_stream_trailer", "set_data_channel_buffered_amount_low_threshold", "load_audio_filter_plugin", "new_apm", "apm_process_stream", "apm_process_reverse_stream", "apm_set_stream_delay", "byte_read_incremental", "byte_read_all", "byte_write_to_file", "text_read_incremental", "text_read_all", "send_file", "send_text", "byte_stream_open", "byte_stream_write", "byte_stream_close", "text_stream_open", "text_stream_write", "text_stream_close", "send_bytes", "set_remote_track_publication_quality", "publish_data_track", "local_data_track_try_push", "local_data_track_unpublish", "local_data_track_is_published", "subscribe_data_track", "remote_data_track_is_published", "data_track_stream_read", "remote_data_track_set_pipeline_options", "define_schema", "get_schema", "simulate_scenario", "new_platform_audio", "get_audio_devices", "set_recording_device", "set_playout_device", "start_recording", "stop_recording", "ready_for_room_event"] | None: ... global___FfiRequest = FfiRequest -@typing_extensions.final +@typing.final class FfiResponse(google.protobuf.message.Message): """This is the output of livekit_ffi_request function.""" @@ -214,28 +491,89 @@ class FfiResponse(google.protobuf.message.Message): UNPUBLISH_TRACK_FIELD_NUMBER: builtins.int PUBLISH_DATA_FIELD_NUMBER: builtins.int SET_SUBSCRIBED_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_METADATA_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_METADATA_FIELD_NUMBER: builtins.int + SET_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_ATTRIBUTES_FIELD_NUMBER: builtins.int GET_SESSION_STATS_FIELD_NUMBER: builtins.int PUBLISH_TRANSCRIPTION_FIELD_NUMBER: builtins.int + PUBLISH_SIP_DTMF_FIELD_NUMBER: builtins.int CREATE_VIDEO_TRACK_FIELD_NUMBER: builtins.int CREATE_AUDIO_TRACK_FIELD_NUMBER: builtins.int + LOCAL_TRACK_MUTE_FIELD_NUMBER: builtins.int + ENABLE_REMOTE_TRACK_FIELD_NUMBER: builtins.int GET_STATS_FIELD_NUMBER: builtins.int + SET_TRACK_SUBSCRIPTION_PERMISSIONS_FIELD_NUMBER: builtins.int NEW_VIDEO_STREAM_FIELD_NUMBER: builtins.int NEW_VIDEO_SOURCE_FIELD_NUMBER: builtins.int CAPTURE_VIDEO_FRAME_FIELD_NUMBER: builtins.int VIDEO_CONVERT_FIELD_NUMBER: builtins.int + VIDEO_STREAM_FROM_PARTICIPANT_FIELD_NUMBER: builtins.int NEW_AUDIO_STREAM_FIELD_NUMBER: builtins.int NEW_AUDIO_SOURCE_FIELD_NUMBER: builtins.int CAPTURE_AUDIO_FRAME_FIELD_NUMBER: builtins.int + CLEAR_AUDIO_BUFFER_FIELD_NUMBER: builtins.int NEW_AUDIO_RESAMPLER_FIELD_NUMBER: builtins.int REMIX_AND_RESAMPLE_FIELD_NUMBER: builtins.int + AUDIO_STREAM_FROM_PARTICIPANT_FIELD_NUMBER: builtins.int E2EE_FIELD_NUMBER: builtins.int + NEW_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + PUSH_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + FLUSH_SOX_RESAMPLER_FIELD_NUMBER: builtins.int + SEND_CHAT_MESSAGE_FIELD_NUMBER: builtins.int + PERFORM_RPC_FIELD_NUMBER: builtins.int + REGISTER_RPC_METHOD_FIELD_NUMBER: builtins.int + UNREGISTER_RPC_METHOD_FIELD_NUMBER: builtins.int + RPC_METHOD_INVOCATION_RESPONSE_FIELD_NUMBER: builtins.int + ENABLE_REMOTE_TRACK_PUBLICATION_FIELD_NUMBER: builtins.int + UPDATE_REMOTE_TRACK_PUBLICATION_DIMENSION_FIELD_NUMBER: builtins.int + SEND_STREAM_HEADER_FIELD_NUMBER: builtins.int + SEND_STREAM_CHUNK_FIELD_NUMBER: builtins.int + SEND_STREAM_TRAILER_FIELD_NUMBER: builtins.int + SET_DATA_CHANNEL_BUFFERED_AMOUNT_LOW_THRESHOLD_FIELD_NUMBER: builtins.int + LOAD_AUDIO_FILTER_PLUGIN_FIELD_NUMBER: builtins.int + NEW_APM_FIELD_NUMBER: builtins.int + APM_PROCESS_STREAM_FIELD_NUMBER: builtins.int + APM_PROCESS_REVERSE_STREAM_FIELD_NUMBER: builtins.int + APM_SET_STREAM_DELAY_FIELD_NUMBER: builtins.int + BYTE_READ_INCREMENTAL_FIELD_NUMBER: builtins.int + BYTE_READ_ALL_FIELD_NUMBER: builtins.int + BYTE_WRITE_TO_FILE_FIELD_NUMBER: builtins.int + TEXT_READ_INCREMENTAL_FIELD_NUMBER: builtins.int + TEXT_READ_ALL_FIELD_NUMBER: builtins.int + SEND_FILE_FIELD_NUMBER: builtins.int + SEND_TEXT_FIELD_NUMBER: builtins.int + BYTE_STREAM_OPEN_FIELD_NUMBER: builtins.int + BYTE_STREAM_WRITE_FIELD_NUMBER: builtins.int + BYTE_STREAM_CLOSE_FIELD_NUMBER: builtins.int + TEXT_STREAM_OPEN_FIELD_NUMBER: builtins.int + TEXT_STREAM_WRITE_FIELD_NUMBER: builtins.int + TEXT_STREAM_CLOSE_FIELD_NUMBER: builtins.int + SEND_BYTES_FIELD_NUMBER: builtins.int + SET_REMOTE_TRACK_PUBLICATION_QUALITY_FIELD_NUMBER: builtins.int + PUBLISH_DATA_TRACK_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_TRY_PUSH_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_UNPUBLISH_FIELD_NUMBER: builtins.int + LOCAL_DATA_TRACK_IS_PUBLISHED_FIELD_NUMBER: builtins.int + SUBSCRIBE_DATA_TRACK_FIELD_NUMBER: builtins.int + REMOTE_DATA_TRACK_IS_PUBLISHED_FIELD_NUMBER: builtins.int + DATA_TRACK_STREAM_READ_FIELD_NUMBER: builtins.int + REMOTE_DATA_TRACK_SET_PIPELINE_OPTIONS_FIELD_NUMBER: builtins.int + DEFINE_SCHEMA_FIELD_NUMBER: builtins.int + GET_SCHEMA_FIELD_NUMBER: builtins.int + SIMULATE_SCENARIO_FIELD_NUMBER: builtins.int + NEW_PLATFORM_AUDIO_FIELD_NUMBER: builtins.int + GET_AUDIO_DEVICES_FIELD_NUMBER: builtins.int + SET_RECORDING_DEVICE_FIELD_NUMBER: builtins.int + SET_PLAYOUT_DEVICE_FIELD_NUMBER: builtins.int + START_RECORDING_FIELD_NUMBER: builtins.int + STOP_RECORDING_FIELD_NUMBER: builtins.int + READY_FOR_ROOM_EVENT_FIELD_NUMBER: builtins.int @property def dispose(self) -> global___DisposeResponse: ... @property def connect(self) -> room_pb2.ConnectResponse: """Room""" + @property def disconnect(self) -> room_pb2.DisconnectResponse: ... @property @@ -247,23 +585,35 @@ class FfiResponse(google.protobuf.message.Message): @property def set_subscribed(self) -> room_pb2.SetSubscribedResponse: ... @property - def update_local_metadata(self) -> room_pb2.UpdateLocalMetadataResponse: ... + def set_local_metadata(self) -> room_pb2.SetLocalMetadataResponse: ... + @property + def set_local_name(self) -> room_pb2.SetLocalNameResponse: ... @property - def update_local_name(self) -> room_pb2.UpdateLocalNameResponse: ... + def set_local_attributes(self) -> room_pb2.SetLocalAttributesResponse: ... @property def get_session_stats(self) -> room_pb2.GetSessionStatsResponse: ... @property def publish_transcription(self) -> room_pb2.PublishTranscriptionResponse: ... @property + def publish_sip_dtmf(self) -> room_pb2.PublishSipDtmfResponse: ... + @property def create_video_track(self) -> track_pb2.CreateVideoTrackResponse: """Track""" + @property def create_audio_track(self) -> track_pb2.CreateAudioTrackResponse: ... @property + def local_track_mute(self) -> track_pb2.LocalTrackMuteResponse: ... + @property + def enable_remote_track(self) -> track_pb2.EnableRemoteTrackResponse: ... + @property def get_stats(self) -> track_pb2.GetStatsResponse: ... @property + def set_track_subscription_permissions(self) -> track_pb2.SetTrackSubscriptionPermissionsResponse: ... + @property def new_video_stream(self) -> video_frame_pb2.NewVideoStreamResponse: """Video""" + @property def new_video_source(self) -> video_frame_pb2.NewVideoSourceResponse: ... @property @@ -271,18 +621,153 @@ class FfiResponse(google.protobuf.message.Message): @property def video_convert(self) -> video_frame_pb2.VideoConvertResponse: ... @property + def video_stream_from_participant(self) -> video_frame_pb2.VideoStreamFromParticipantResponse: ... + @property def new_audio_stream(self) -> audio_frame_pb2.NewAudioStreamResponse: """Audio""" + @property def new_audio_source(self) -> audio_frame_pb2.NewAudioSourceResponse: ... @property def capture_audio_frame(self) -> audio_frame_pb2.CaptureAudioFrameResponse: ... @property + def clear_audio_buffer(self) -> audio_frame_pb2.ClearAudioBufferResponse: ... + @property def new_audio_resampler(self) -> audio_frame_pb2.NewAudioResamplerResponse: ... @property def remix_and_resample(self) -> audio_frame_pb2.RemixAndResampleResponse: ... @property + def audio_stream_from_participant(self) -> audio_frame_pb2.AudioStreamFromParticipantResponse: ... + @property def e2ee(self) -> e2ee_pb2.E2eeResponse: ... + @property + def new_sox_resampler(self) -> audio_frame_pb2.NewSoxResamplerResponse: ... + @property + def push_sox_resampler(self) -> audio_frame_pb2.PushSoxResamplerResponse: ... + @property + def flush_sox_resampler(self) -> audio_frame_pb2.FlushSoxResamplerResponse: ... + @property + def send_chat_message(self) -> room_pb2.SendChatMessageResponse: ... + @property + def perform_rpc(self) -> rpc_pb2.PerformRpcResponse: + """RPC""" + + @property + def register_rpc_method(self) -> rpc_pb2.RegisterRpcMethodResponse: ... + @property + def unregister_rpc_method(self) -> rpc_pb2.UnregisterRpcMethodResponse: ... + @property + def rpc_method_invocation_response(self) -> rpc_pb2.RpcMethodInvocationResponseResponse: ... + @property + def enable_remote_track_publication(self) -> track_publication_pb2.EnableRemoteTrackPublicationResponse: + """Track Publication""" + + @property + def update_remote_track_publication_dimension(self) -> track_publication_pb2.UpdateRemoteTrackPublicationDimensionResponse: ... + @property + def send_stream_header(self) -> room_pb2.SendStreamHeaderResponse: + """Data Streams""" + + @property + def send_stream_chunk(self) -> room_pb2.SendStreamChunkResponse: ... + @property + def send_stream_trailer(self) -> room_pb2.SendStreamTrailerResponse: ... + @property + def set_data_channel_buffered_amount_low_threshold(self) -> room_pb2.SetDataChannelBufferedAmountLowThresholdResponse: + """Data Channel""" + + @property + def load_audio_filter_plugin(self) -> audio_frame_pb2.LoadAudioFilterPluginResponse: + """Audio Filter Plugin""" + + @property + def new_apm(self) -> audio_frame_pb2.NewApmResponse: ... + @property + def apm_process_stream(self) -> audio_frame_pb2.ApmProcessStreamResponse: ... + @property + def apm_process_reverse_stream(self) -> audio_frame_pb2.ApmProcessReverseStreamResponse: ... + @property + def apm_set_stream_delay(self) -> audio_frame_pb2.ApmSetStreamDelayResponse: ... + @property + def byte_read_incremental(self) -> data_stream_pb2.ByteStreamReaderReadIncrementalResponse: + """Data Streams (high level)""" + + @property + def byte_read_all(self) -> data_stream_pb2.ByteStreamReaderReadAllResponse: ... + @property + def byte_write_to_file(self) -> data_stream_pb2.ByteStreamReaderWriteToFileResponse: ... + @property + def text_read_incremental(self) -> data_stream_pb2.TextStreamReaderReadIncrementalResponse: ... + @property + def text_read_all(self) -> data_stream_pb2.TextStreamReaderReadAllResponse: ... + @property + def send_file(self) -> data_stream_pb2.StreamSendFileResponse: ... + @property + def send_text(self) -> data_stream_pb2.StreamSendTextResponse: ... + @property + def byte_stream_open(self) -> data_stream_pb2.ByteStreamOpenResponse: ... + @property + def byte_stream_write(self) -> data_stream_pb2.ByteStreamWriterWriteResponse: ... + @property + def byte_stream_close(self) -> data_stream_pb2.ByteStreamWriterCloseResponse: ... + @property + def text_stream_open(self) -> data_stream_pb2.TextStreamOpenResponse: ... + @property + def text_stream_write(self) -> data_stream_pb2.TextStreamWriterWriteResponse: ... + @property + def text_stream_close(self) -> data_stream_pb2.TextStreamWriterCloseResponse: ... + @property + def send_bytes(self) -> data_stream_pb2.StreamSendBytesResponse: ... + @property + def set_remote_track_publication_quality(self) -> track_publication_pb2.SetRemoteTrackPublicationQualityResponse: ... + @property + def publish_data_track(self) -> data_track_pb2.PublishDataTrackResponse: + """Data Track (local)""" + + @property + def local_data_track_try_push(self) -> data_track_pb2.LocalDataTrackTryPushResponse: ... + @property + def local_data_track_unpublish(self) -> data_track_pb2.LocalDataTrackUnpublishResponse: ... + @property + def local_data_track_is_published(self) -> data_track_pb2.LocalDataTrackIsPublishedResponse: ... + @property + def subscribe_data_track(self) -> data_track_pb2.SubscribeDataTrackResponse: + """Data Track (remote)""" + + @property + def remote_data_track_is_published(self) -> data_track_pb2.RemoteDataTrackIsPublishedResponse: ... + @property + def data_track_stream_read(self) -> data_track_pb2.DataTrackStreamReadResponse: ... + @property + def remote_data_track_set_pipeline_options(self) -> data_track_pb2.RemoteDataTrackSetPipelineOptionsResponse: ... + @property + def define_schema(self) -> data_track_pb2.DefineSchemaResponse: + """Data Track (schemas)""" + + @property + def get_schema(self) -> data_track_pb2.GetSchemaResponse: ... + @property + def simulate_scenario(self) -> room_pb2.SimulateScenarioResponse: + """Reconnection / chaos testing""" + + @property + def new_platform_audio(self) -> audio_frame_pb2.NewPlatformAudioResponse: + """Platform Audio (ADM)""" + + @property + def get_audio_devices(self) -> audio_frame_pb2.GetAudioDevicesResponse: ... + @property + def set_recording_device(self) -> audio_frame_pb2.SetRecordingDeviceResponse: ... + @property + def set_playout_device(self) -> audio_frame_pb2.SetPlayoutDeviceResponse: ... + @property + def start_recording(self) -> audio_frame_pb2.StartRecordingResponse: ... + @property + def stop_recording(self) -> audio_frame_pb2.StopRecordingResponse: ... + @property + def ready_for_room_event(self) -> room_pb2.ReadyForRoomEventResponse: + """Room event ready signal""" + def __init__( self, *, @@ -293,31 +778,91 @@ class FfiResponse(google.protobuf.message.Message): unpublish_track: room_pb2.UnpublishTrackResponse | None = ..., publish_data: room_pb2.PublishDataResponse | None = ..., set_subscribed: room_pb2.SetSubscribedResponse | None = ..., - update_local_metadata: room_pb2.UpdateLocalMetadataResponse | None = ..., - update_local_name: room_pb2.UpdateLocalNameResponse | None = ..., + set_local_metadata: room_pb2.SetLocalMetadataResponse | None = ..., + set_local_name: room_pb2.SetLocalNameResponse | None = ..., + set_local_attributes: room_pb2.SetLocalAttributesResponse | None = ..., get_session_stats: room_pb2.GetSessionStatsResponse | None = ..., publish_transcription: room_pb2.PublishTranscriptionResponse | None = ..., + publish_sip_dtmf: room_pb2.PublishSipDtmfResponse | None = ..., create_video_track: track_pb2.CreateVideoTrackResponse | None = ..., create_audio_track: track_pb2.CreateAudioTrackResponse | None = ..., + local_track_mute: track_pb2.LocalTrackMuteResponse | None = ..., + enable_remote_track: track_pb2.EnableRemoteTrackResponse | None = ..., get_stats: track_pb2.GetStatsResponse | None = ..., + set_track_subscription_permissions: track_pb2.SetTrackSubscriptionPermissionsResponse | None = ..., new_video_stream: video_frame_pb2.NewVideoStreamResponse | None = ..., new_video_source: video_frame_pb2.NewVideoSourceResponse | None = ..., capture_video_frame: video_frame_pb2.CaptureVideoFrameResponse | None = ..., video_convert: video_frame_pb2.VideoConvertResponse | None = ..., + video_stream_from_participant: video_frame_pb2.VideoStreamFromParticipantResponse | None = ..., new_audio_stream: audio_frame_pb2.NewAudioStreamResponse | None = ..., new_audio_source: audio_frame_pb2.NewAudioSourceResponse | None = ..., capture_audio_frame: audio_frame_pb2.CaptureAudioFrameResponse | None = ..., + clear_audio_buffer: audio_frame_pb2.ClearAudioBufferResponse | None = ..., new_audio_resampler: audio_frame_pb2.NewAudioResamplerResponse | None = ..., remix_and_resample: audio_frame_pb2.RemixAndResampleResponse | None = ..., + audio_stream_from_participant: audio_frame_pb2.AudioStreamFromParticipantResponse | None = ..., e2ee: e2ee_pb2.E2eeResponse | None = ..., + new_sox_resampler: audio_frame_pb2.NewSoxResamplerResponse | None = ..., + push_sox_resampler: audio_frame_pb2.PushSoxResamplerResponse | None = ..., + flush_sox_resampler: audio_frame_pb2.FlushSoxResamplerResponse | None = ..., + send_chat_message: room_pb2.SendChatMessageResponse | None = ..., + perform_rpc: rpc_pb2.PerformRpcResponse | None = ..., + register_rpc_method: rpc_pb2.RegisterRpcMethodResponse | None = ..., + unregister_rpc_method: rpc_pb2.UnregisterRpcMethodResponse | None = ..., + rpc_method_invocation_response: rpc_pb2.RpcMethodInvocationResponseResponse | None = ..., + enable_remote_track_publication: track_publication_pb2.EnableRemoteTrackPublicationResponse | None = ..., + update_remote_track_publication_dimension: track_publication_pb2.UpdateRemoteTrackPublicationDimensionResponse | None = ..., + send_stream_header: room_pb2.SendStreamHeaderResponse | None = ..., + send_stream_chunk: room_pb2.SendStreamChunkResponse | None = ..., + send_stream_trailer: room_pb2.SendStreamTrailerResponse | None = ..., + set_data_channel_buffered_amount_low_threshold: room_pb2.SetDataChannelBufferedAmountLowThresholdResponse | None = ..., + load_audio_filter_plugin: audio_frame_pb2.LoadAudioFilterPluginResponse | None = ..., + new_apm: audio_frame_pb2.NewApmResponse | None = ..., + apm_process_stream: audio_frame_pb2.ApmProcessStreamResponse | None = ..., + apm_process_reverse_stream: audio_frame_pb2.ApmProcessReverseStreamResponse | None = ..., + apm_set_stream_delay: audio_frame_pb2.ApmSetStreamDelayResponse | None = ..., + byte_read_incremental: data_stream_pb2.ByteStreamReaderReadIncrementalResponse | None = ..., + byte_read_all: data_stream_pb2.ByteStreamReaderReadAllResponse | None = ..., + byte_write_to_file: data_stream_pb2.ByteStreamReaderWriteToFileResponse | None = ..., + text_read_incremental: data_stream_pb2.TextStreamReaderReadIncrementalResponse | None = ..., + text_read_all: data_stream_pb2.TextStreamReaderReadAllResponse | None = ..., + send_file: data_stream_pb2.StreamSendFileResponse | None = ..., + send_text: data_stream_pb2.StreamSendTextResponse | None = ..., + byte_stream_open: data_stream_pb2.ByteStreamOpenResponse | None = ..., + byte_stream_write: data_stream_pb2.ByteStreamWriterWriteResponse | None = ..., + byte_stream_close: data_stream_pb2.ByteStreamWriterCloseResponse | None = ..., + text_stream_open: data_stream_pb2.TextStreamOpenResponse | None = ..., + text_stream_write: data_stream_pb2.TextStreamWriterWriteResponse | None = ..., + text_stream_close: data_stream_pb2.TextStreamWriterCloseResponse | None = ..., + send_bytes: data_stream_pb2.StreamSendBytesResponse | None = ..., + set_remote_track_publication_quality: track_publication_pb2.SetRemoteTrackPublicationQualityResponse | None = ..., + publish_data_track: data_track_pb2.PublishDataTrackResponse | None = ..., + local_data_track_try_push: data_track_pb2.LocalDataTrackTryPushResponse | None = ..., + local_data_track_unpublish: data_track_pb2.LocalDataTrackUnpublishResponse | None = ..., + local_data_track_is_published: data_track_pb2.LocalDataTrackIsPublishedResponse | None = ..., + subscribe_data_track: data_track_pb2.SubscribeDataTrackResponse | None = ..., + remote_data_track_is_published: data_track_pb2.RemoteDataTrackIsPublishedResponse | None = ..., + data_track_stream_read: data_track_pb2.DataTrackStreamReadResponse | None = ..., + remote_data_track_set_pipeline_options: data_track_pb2.RemoteDataTrackSetPipelineOptionsResponse | None = ..., + define_schema: data_track_pb2.DefineSchemaResponse | None = ..., + get_schema: data_track_pb2.GetSchemaResponse | None = ..., + simulate_scenario: room_pb2.SimulateScenarioResponse | None = ..., + new_platform_audio: audio_frame_pb2.NewPlatformAudioResponse | None = ..., + get_audio_devices: audio_frame_pb2.GetAudioDevicesResponse | None = ..., + set_recording_device: audio_frame_pb2.SetRecordingDeviceResponse | None = ..., + set_playout_device: audio_frame_pb2.SetPlayoutDeviceResponse | None = ..., + start_recording: audio_frame_pb2.StartRecordingResponse | None = ..., + stop_recording: audio_frame_pb2.StopRecordingResponse | None = ..., + ready_for_room_event: room_pb2.ReadyForRoomEventResponse | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "message", b"message", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "remix_and_resample", b"remix_and_resample", "set_subscribed", b"set_subscribed", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_convert", b"video_convert"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "message", b"message", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "remix_and_resample", b"remix_and_resample", "set_subscribed", b"set_subscribed", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_convert", b"video_convert"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["dispose", "connect", "disconnect", "publish_track", "unpublish_track", "publish_data", "set_subscribed", "update_local_metadata", "update_local_name", "get_session_stats", "publish_transcription", "create_video_track", "create_audio_track", "get_stats", "new_video_stream", "new_video_source", "capture_video_frame", "video_convert", "new_audio_stream", "new_audio_source", "capture_audio_frame", "new_audio_resampler", "remix_and_resample", "e2ee"] | None: ... + def HasField(self, field_name: typing.Literal["apm_process_reverse_stream", b"apm_process_reverse_stream", "apm_process_stream", b"apm_process_stream", "apm_set_stream_delay", b"apm_set_stream_delay", "audio_stream_from_participant", b"audio_stream_from_participant", "byte_read_all", b"byte_read_all", "byte_read_incremental", b"byte_read_incremental", "byte_stream_close", b"byte_stream_close", "byte_stream_open", b"byte_stream_open", "byte_stream_write", b"byte_stream_write", "byte_write_to_file", b"byte_write_to_file", "capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "clear_audio_buffer", b"clear_audio_buffer", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "data_track_stream_read", b"data_track_stream_read", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "enable_remote_track", b"enable_remote_track", "enable_remote_track_publication", b"enable_remote_track_publication", "flush_sox_resampler", b"flush_sox_resampler", "get_audio_devices", b"get_audio_devices", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "load_audio_filter_plugin", b"load_audio_filter_plugin", "local_data_track_is_published", b"local_data_track_is_published", "local_data_track_try_push", b"local_data_track_try_push", "local_data_track_unpublish", b"local_data_track_unpublish", "local_track_mute", b"local_track_mute", "message", b"message", "new_apm", b"new_apm", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_platform_audio", b"new_platform_audio", "new_sox_resampler", b"new_sox_resampler", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "push_sox_resampler", b"push_sox_resampler", "ready_for_room_event", b"ready_for_room_event", "register_rpc_method", b"register_rpc_method", "remix_and_resample", b"remix_and_resample", "remote_data_track_is_published", b"remote_data_track_is_published", "remote_data_track_set_pipeline_options", b"remote_data_track_set_pipeline_options", "rpc_method_invocation_response", b"rpc_method_invocation_response", "send_bytes", b"send_bytes", "send_chat_message", b"send_chat_message", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_data_channel_buffered_amount_low_threshold", b"set_data_channel_buffered_amount_low_threshold", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "set_playout_device", b"set_playout_device", "set_recording_device", b"set_recording_device", "set_remote_track_publication_quality", b"set_remote_track_publication_quality", "set_subscribed", b"set_subscribed", "set_track_subscription_permissions", b"set_track_subscription_permissions", "simulate_scenario", b"simulate_scenario", "start_recording", b"start_recording", "stop_recording", b"stop_recording", "subscribe_data_track", b"subscribe_data_track", "text_read_all", b"text_read_all", "text_read_incremental", b"text_read_incremental", "text_stream_close", b"text_stream_close", "text_stream_open", b"text_stream_open", "text_stream_write", b"text_stream_write", "unpublish_track", b"unpublish_track", "unregister_rpc_method", b"unregister_rpc_method", "update_remote_track_publication_dimension", b"update_remote_track_publication_dimension", "video_convert", b"video_convert", "video_stream_from_participant", b"video_stream_from_participant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["apm_process_reverse_stream", b"apm_process_reverse_stream", "apm_process_stream", b"apm_process_stream", "apm_set_stream_delay", b"apm_set_stream_delay", "audio_stream_from_participant", b"audio_stream_from_participant", "byte_read_all", b"byte_read_all", "byte_read_incremental", b"byte_read_incremental", "byte_stream_close", b"byte_stream_close", "byte_stream_open", b"byte_stream_open", "byte_stream_write", b"byte_stream_write", "byte_write_to_file", b"byte_write_to_file", "capture_audio_frame", b"capture_audio_frame", "capture_video_frame", b"capture_video_frame", "clear_audio_buffer", b"clear_audio_buffer", "connect", b"connect", "create_audio_track", b"create_audio_track", "create_video_track", b"create_video_track", "data_track_stream_read", b"data_track_stream_read", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "e2ee", b"e2ee", "enable_remote_track", b"enable_remote_track", "enable_remote_track_publication", b"enable_remote_track_publication", "flush_sox_resampler", b"flush_sox_resampler", "get_audio_devices", b"get_audio_devices", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "load_audio_filter_plugin", b"load_audio_filter_plugin", "local_data_track_is_published", b"local_data_track_is_published", "local_data_track_try_push", b"local_data_track_try_push", "local_data_track_unpublish", b"local_data_track_unpublish", "local_track_mute", b"local_track_mute", "message", b"message", "new_apm", b"new_apm", "new_audio_resampler", b"new_audio_resampler", "new_audio_source", b"new_audio_source", "new_audio_stream", b"new_audio_stream", "new_platform_audio", b"new_platform_audio", "new_sox_resampler", b"new_sox_resampler", "new_video_source", b"new_video_source", "new_video_stream", b"new_video_stream", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "push_sox_resampler", b"push_sox_resampler", "ready_for_room_event", b"ready_for_room_event", "register_rpc_method", b"register_rpc_method", "remix_and_resample", b"remix_and_resample", "remote_data_track_is_published", b"remote_data_track_is_published", "remote_data_track_set_pipeline_options", b"remote_data_track_set_pipeline_options", "rpc_method_invocation_response", b"rpc_method_invocation_response", "send_bytes", b"send_bytes", "send_chat_message", b"send_chat_message", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_data_channel_buffered_amount_low_threshold", b"set_data_channel_buffered_amount_low_threshold", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "set_playout_device", b"set_playout_device", "set_recording_device", b"set_recording_device", "set_remote_track_publication_quality", b"set_remote_track_publication_quality", "set_subscribed", b"set_subscribed", "set_track_subscription_permissions", b"set_track_subscription_permissions", "simulate_scenario", b"simulate_scenario", "start_recording", b"start_recording", "stop_recording", b"stop_recording", "subscribe_data_track", b"subscribe_data_track", "text_read_all", b"text_read_all", "text_read_incremental", b"text_read_incremental", "text_stream_close", b"text_stream_close", "text_stream_open", b"text_stream_open", "text_stream_write", b"text_stream_write", "unpublish_track", b"unpublish_track", "unregister_rpc_method", b"unregister_rpc_method", "update_remote_track_publication_dimension", b"update_remote_track_publication_dimension", "video_convert", b"video_convert", "video_stream_from_participant", b"video_stream_from_participant"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["dispose", "connect", "disconnect", "publish_track", "unpublish_track", "publish_data", "set_subscribed", "set_local_metadata", "set_local_name", "set_local_attributes", "get_session_stats", "publish_transcription", "publish_sip_dtmf", "create_video_track", "create_audio_track", "local_track_mute", "enable_remote_track", "get_stats", "set_track_subscription_permissions", "new_video_stream", "new_video_source", "capture_video_frame", "video_convert", "video_stream_from_participant", "new_audio_stream", "new_audio_source", "capture_audio_frame", "clear_audio_buffer", "new_audio_resampler", "remix_and_resample", "audio_stream_from_participant", "e2ee", "new_sox_resampler", "push_sox_resampler", "flush_sox_resampler", "send_chat_message", "perform_rpc", "register_rpc_method", "unregister_rpc_method", "rpc_method_invocation_response", "enable_remote_track_publication", "update_remote_track_publication_dimension", "send_stream_header", "send_stream_chunk", "send_stream_trailer", "set_data_channel_buffered_amount_low_threshold", "load_audio_filter_plugin", "new_apm", "apm_process_stream", "apm_process_reverse_stream", "apm_set_stream_delay", "byte_read_incremental", "byte_read_all", "byte_write_to_file", "text_read_incremental", "text_read_all", "send_file", "send_text", "byte_stream_open", "byte_stream_write", "byte_stream_close", "text_stream_open", "text_stream_write", "text_stream_close", "send_bytes", "set_remote_track_publication_quality", "publish_data_track", "local_data_track_try_push", "local_data_track_unpublish", "local_data_track_is_published", "subscribe_data_track", "remote_data_track_is_published", "data_track_stream_read", "remote_data_track_set_pipeline_options", "define_schema", "get_schema", "simulate_scenario", "new_platform_audio", "get_audio_devices", "set_recording_device", "set_playout_device", "start_recording", "stop_recording", "ready_for_room_event"] | None: ... global___FfiResponse = FfiResponse -@typing_extensions.final +@typing.final class FfiEvent(google.protobuf.message.Message): """To minimize complexity, participant events are not included in the protocol. It is easily deducible from the room events and it turned out that is is easier to implement @@ -338,12 +883,39 @@ class FfiEvent(google.protobuf.message.Message): PUBLISH_DATA_FIELD_NUMBER: builtins.int PUBLISH_TRANSCRIPTION_FIELD_NUMBER: builtins.int CAPTURE_AUDIO_FRAME_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_METADATA_FIELD_NUMBER: builtins.int - UPDATE_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_METADATA_FIELD_NUMBER: builtins.int + SET_LOCAL_NAME_FIELD_NUMBER: builtins.int + SET_LOCAL_ATTRIBUTES_FIELD_NUMBER: builtins.int GET_STATS_FIELD_NUMBER: builtins.int LOGS_FIELD_NUMBER: builtins.int GET_SESSION_STATS_FIELD_NUMBER: builtins.int PANIC_FIELD_NUMBER: builtins.int + PUBLISH_SIP_DTMF_FIELD_NUMBER: builtins.int + CHAT_MESSAGE_FIELD_NUMBER: builtins.int + PERFORM_RPC_FIELD_NUMBER: builtins.int + RPC_METHOD_INVOCATION_FIELD_NUMBER: builtins.int + SEND_STREAM_HEADER_FIELD_NUMBER: builtins.int + SEND_STREAM_CHUNK_FIELD_NUMBER: builtins.int + SEND_STREAM_TRAILER_FIELD_NUMBER: builtins.int + BYTE_STREAM_READER_EVENT_FIELD_NUMBER: builtins.int + BYTE_STREAM_READER_READ_ALL_FIELD_NUMBER: builtins.int + BYTE_STREAM_READER_WRITE_TO_FILE_FIELD_NUMBER: builtins.int + BYTE_STREAM_OPEN_FIELD_NUMBER: builtins.int + BYTE_STREAM_WRITER_WRITE_FIELD_NUMBER: builtins.int + BYTE_STREAM_WRITER_CLOSE_FIELD_NUMBER: builtins.int + SEND_FILE_FIELD_NUMBER: builtins.int + TEXT_STREAM_READER_EVENT_FIELD_NUMBER: builtins.int + TEXT_STREAM_READER_READ_ALL_FIELD_NUMBER: builtins.int + TEXT_STREAM_OPEN_FIELD_NUMBER: builtins.int + TEXT_STREAM_WRITER_WRITE_FIELD_NUMBER: builtins.int + TEXT_STREAM_WRITER_CLOSE_FIELD_NUMBER: builtins.int + SEND_TEXT_FIELD_NUMBER: builtins.int + SEND_BYTES_FIELD_NUMBER: builtins.int + PUBLISH_DATA_TRACK_FIELD_NUMBER: builtins.int + DATA_TRACK_STREAM_EVENT_FIELD_NUMBER: builtins.int + SIMULATE_SCENARIO_FIELD_NUMBER: builtins.int + DEFINE_SCHEMA_FIELD_NUMBER: builtins.int + GET_SCHEMA_FIELD_NUMBER: builtins.int @property def room_event(self) -> room_pb2.RoomEvent: ... @property @@ -369,9 +941,11 @@ class FfiEvent(google.protobuf.message.Message): @property def capture_audio_frame(self) -> audio_frame_pb2.CaptureAudioFrameCallback: ... @property - def update_local_metadata(self) -> room_pb2.UpdateLocalMetadataCallback: ... + def set_local_metadata(self) -> room_pb2.SetLocalMetadataCallback: ... + @property + def set_local_name(self) -> room_pb2.SetLocalNameCallback: ... @property - def update_local_name(self) -> room_pb2.UpdateLocalNameCallback: ... + def set_local_attributes(self) -> room_pb2.SetLocalAttributesCallback: ... @property def get_stats(self) -> track_pb2.GetStatsCallback: ... @property @@ -380,6 +954,68 @@ class FfiEvent(google.protobuf.message.Message): def get_session_stats(self) -> room_pb2.GetSessionStatsCallback: ... @property def panic(self) -> global___Panic: ... + @property + def publish_sip_dtmf(self) -> room_pb2.PublishSipDtmfCallback: ... + @property + def chat_message(self) -> room_pb2.SendChatMessageCallback: ... + @property + def perform_rpc(self) -> rpc_pb2.PerformRpcCallback: ... + @property + def rpc_method_invocation(self) -> rpc_pb2.RpcMethodInvocationEvent: ... + @property + def send_stream_header(self) -> room_pb2.SendStreamHeaderCallback: + """Data Streams (low level)""" + + @property + def send_stream_chunk(self) -> room_pb2.SendStreamChunkCallback: ... + @property + def send_stream_trailer(self) -> room_pb2.SendStreamTrailerCallback: ... + @property + def byte_stream_reader_event(self) -> data_stream_pb2.ByteStreamReaderEvent: + """Data Streams (high level)""" + + @property + def byte_stream_reader_read_all(self) -> data_stream_pb2.ByteStreamReaderReadAllCallback: ... + @property + def byte_stream_reader_write_to_file(self) -> data_stream_pb2.ByteStreamReaderWriteToFileCallback: ... + @property + def byte_stream_open(self) -> data_stream_pb2.ByteStreamOpenCallback: ... + @property + def byte_stream_writer_write(self) -> data_stream_pb2.ByteStreamWriterWriteCallback: ... + @property + def byte_stream_writer_close(self) -> data_stream_pb2.ByteStreamWriterCloseCallback: ... + @property + def send_file(self) -> data_stream_pb2.StreamSendFileCallback: ... + @property + def text_stream_reader_event(self) -> data_stream_pb2.TextStreamReaderEvent: ... + @property + def text_stream_reader_read_all(self) -> data_stream_pb2.TextStreamReaderReadAllCallback: ... + @property + def text_stream_open(self) -> data_stream_pb2.TextStreamOpenCallback: ... + @property + def text_stream_writer_write(self) -> data_stream_pb2.TextStreamWriterWriteCallback: ... + @property + def text_stream_writer_close(self) -> data_stream_pb2.TextStreamWriterCloseCallback: ... + @property + def send_text(self) -> data_stream_pb2.StreamSendTextCallback: ... + @property + def send_bytes(self) -> data_stream_pb2.StreamSendBytesCallback: ... + @property + def publish_data_track(self) -> data_track_pb2.PublishDataTrackCallback: + """Data Track (local)""" + + @property + def data_track_stream_event(self) -> data_track_pb2.DataTrackStreamEvent: + """Data Track (remote)""" + + @property + def simulate_scenario(self) -> room_pb2.SimulateScenarioCallback: ... + @property + def define_schema(self) -> data_track_pb2.DefineSchemaCallback: + """Data Track (schemas)""" + + @property + def get_schema(self) -> data_track_pb2.GetSchemaCallback: ... def __init__( self, *, @@ -395,20 +1031,47 @@ class FfiEvent(google.protobuf.message.Message): publish_data: room_pb2.PublishDataCallback | None = ..., publish_transcription: room_pb2.PublishTranscriptionCallback | None = ..., capture_audio_frame: audio_frame_pb2.CaptureAudioFrameCallback | None = ..., - update_local_metadata: room_pb2.UpdateLocalMetadataCallback | None = ..., - update_local_name: room_pb2.UpdateLocalNameCallback | None = ..., + set_local_metadata: room_pb2.SetLocalMetadataCallback | None = ..., + set_local_name: room_pb2.SetLocalNameCallback | None = ..., + set_local_attributes: room_pb2.SetLocalAttributesCallback | None = ..., get_stats: track_pb2.GetStatsCallback | None = ..., logs: global___LogBatch | None = ..., get_session_stats: room_pb2.GetSessionStatsCallback | None = ..., panic: global___Panic | None = ..., + publish_sip_dtmf: room_pb2.PublishSipDtmfCallback | None = ..., + chat_message: room_pb2.SendChatMessageCallback | None = ..., + perform_rpc: rpc_pb2.PerformRpcCallback | None = ..., + rpc_method_invocation: rpc_pb2.RpcMethodInvocationEvent | None = ..., + send_stream_header: room_pb2.SendStreamHeaderCallback | None = ..., + send_stream_chunk: room_pb2.SendStreamChunkCallback | None = ..., + send_stream_trailer: room_pb2.SendStreamTrailerCallback | None = ..., + byte_stream_reader_event: data_stream_pb2.ByteStreamReaderEvent | None = ..., + byte_stream_reader_read_all: data_stream_pb2.ByteStreamReaderReadAllCallback | None = ..., + byte_stream_reader_write_to_file: data_stream_pb2.ByteStreamReaderWriteToFileCallback | None = ..., + byte_stream_open: data_stream_pb2.ByteStreamOpenCallback | None = ..., + byte_stream_writer_write: data_stream_pb2.ByteStreamWriterWriteCallback | None = ..., + byte_stream_writer_close: data_stream_pb2.ByteStreamWriterCloseCallback | None = ..., + send_file: data_stream_pb2.StreamSendFileCallback | None = ..., + text_stream_reader_event: data_stream_pb2.TextStreamReaderEvent | None = ..., + text_stream_reader_read_all: data_stream_pb2.TextStreamReaderReadAllCallback | None = ..., + text_stream_open: data_stream_pb2.TextStreamOpenCallback | None = ..., + text_stream_writer_write: data_stream_pb2.TextStreamWriterWriteCallback | None = ..., + text_stream_writer_close: data_stream_pb2.TextStreamWriterCloseCallback | None = ..., + send_text: data_stream_pb2.StreamSendTextCallback | None = ..., + send_bytes: data_stream_pb2.StreamSendBytesCallback | None = ..., + publish_data_track: data_track_pb2.PublishDataTrackCallback | None = ..., + data_track_stream_event: data_track_pb2.DataTrackStreamEvent | None = ..., + simulate_scenario: room_pb2.SimulateScenarioCallback | None = ..., + define_schema: data_track_pb2.DefineSchemaCallback | None = ..., + get_schema: data_track_pb2.GetSchemaCallback | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["audio_stream_event", b"audio_stream_event", "capture_audio_frame", b"capture_audio_frame", "connect", b"connect", "disconnect", b"disconnect", "dispose", b"dispose", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "logs", b"logs", "message", b"message", "panic", b"panic", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "room_event", b"room_event", "track_event", b"track_event", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_stream_event", b"video_stream_event"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["audio_stream_event", b"audio_stream_event", "capture_audio_frame", b"capture_audio_frame", "connect", b"connect", "disconnect", b"disconnect", "dispose", b"dispose", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "logs", b"logs", "message", b"message", "panic", b"panic", "publish_data", b"publish_data", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "room_event", b"room_event", "track_event", b"track_event", "unpublish_track", b"unpublish_track", "update_local_metadata", b"update_local_metadata", "update_local_name", b"update_local_name", "video_stream_event", b"video_stream_event"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["room_event", "track_event", "video_stream_event", "audio_stream_event", "connect", "disconnect", "dispose", "publish_track", "unpublish_track", "publish_data", "publish_transcription", "capture_audio_frame", "update_local_metadata", "update_local_name", "get_stats", "logs", "get_session_stats", "panic"] | None: ... + def HasField(self, field_name: typing.Literal["audio_stream_event", b"audio_stream_event", "byte_stream_open", b"byte_stream_open", "byte_stream_reader_event", b"byte_stream_reader_event", "byte_stream_reader_read_all", b"byte_stream_reader_read_all", "byte_stream_reader_write_to_file", b"byte_stream_reader_write_to_file", "byte_stream_writer_close", b"byte_stream_writer_close", "byte_stream_writer_write", b"byte_stream_writer_write", "capture_audio_frame", b"capture_audio_frame", "chat_message", b"chat_message", "connect", b"connect", "data_track_stream_event", b"data_track_stream_event", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "logs", b"logs", "message", b"message", "panic", b"panic", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "room_event", b"room_event", "rpc_method_invocation", b"rpc_method_invocation", "send_bytes", b"send_bytes", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "simulate_scenario", b"simulate_scenario", "text_stream_open", b"text_stream_open", "text_stream_reader_event", b"text_stream_reader_event", "text_stream_reader_read_all", b"text_stream_reader_read_all", "text_stream_writer_close", b"text_stream_writer_close", "text_stream_writer_write", b"text_stream_writer_write", "track_event", b"track_event", "unpublish_track", b"unpublish_track", "video_stream_event", b"video_stream_event"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_stream_event", b"audio_stream_event", "byte_stream_open", b"byte_stream_open", "byte_stream_reader_event", b"byte_stream_reader_event", "byte_stream_reader_read_all", b"byte_stream_reader_read_all", "byte_stream_reader_write_to_file", b"byte_stream_reader_write_to_file", "byte_stream_writer_close", b"byte_stream_writer_close", "byte_stream_writer_write", b"byte_stream_writer_write", "capture_audio_frame", b"capture_audio_frame", "chat_message", b"chat_message", "connect", b"connect", "data_track_stream_event", b"data_track_stream_event", "define_schema", b"define_schema", "disconnect", b"disconnect", "dispose", b"dispose", "get_schema", b"get_schema", "get_session_stats", b"get_session_stats", "get_stats", b"get_stats", "logs", b"logs", "message", b"message", "panic", b"panic", "perform_rpc", b"perform_rpc", "publish_data", b"publish_data", "publish_data_track", b"publish_data_track", "publish_sip_dtmf", b"publish_sip_dtmf", "publish_track", b"publish_track", "publish_transcription", b"publish_transcription", "room_event", b"room_event", "rpc_method_invocation", b"rpc_method_invocation", "send_bytes", b"send_bytes", "send_file", b"send_file", "send_stream_chunk", b"send_stream_chunk", "send_stream_header", b"send_stream_header", "send_stream_trailer", b"send_stream_trailer", "send_text", b"send_text", "set_local_attributes", b"set_local_attributes", "set_local_metadata", b"set_local_metadata", "set_local_name", b"set_local_name", "simulate_scenario", b"simulate_scenario", "text_stream_open", b"text_stream_open", "text_stream_reader_event", b"text_stream_reader_event", "text_stream_reader_read_all", b"text_stream_reader_read_all", "text_stream_writer_close", b"text_stream_writer_close", "text_stream_writer_write", b"text_stream_writer_write", "track_event", b"track_event", "unpublish_track", b"unpublish_track", "video_stream_event", b"video_stream_event"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["room_event", "track_event", "video_stream_event", "audio_stream_event", "connect", "disconnect", "dispose", "publish_track", "unpublish_track", "publish_data", "publish_transcription", "capture_audio_frame", "set_local_metadata", "set_local_name", "set_local_attributes", "get_stats", "logs", "get_session_stats", "panic", "publish_sip_dtmf", "chat_message", "perform_rpc", "rpc_method_invocation", "send_stream_header", "send_stream_chunk", "send_stream_trailer", "byte_stream_reader_event", "byte_stream_reader_read_all", "byte_stream_reader_write_to_file", "byte_stream_open", "byte_stream_writer_write", "byte_stream_writer_close", "send_file", "text_stream_reader_event", "text_stream_reader_read_all", "text_stream_open", "text_stream_writer_write", "text_stream_writer_close", "send_text", "send_bytes", "publish_data_track", "data_track_stream_event", "simulate_scenario", "define_schema", "get_schema"] | None: ... global___FfiEvent = FfiEvent -@typing_extensions.final +@typing.final class DisposeRequest(google.protobuf.message.Message): """Stop all rooms synchronously (Do we need async here?). e.g: This is used for the Unity Editor after each assemblies reload. @@ -421,11 +1084,12 @@ class DisposeRequest(google.protobuf.message.Message): def __init__( self, ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async", b"async"]) -> None: ... + def HasField(self, field_name: typing.Literal["async", b"async"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async", b"async"]) -> None: ... global___DisposeRequest = DisposeRequest -@typing_extensions.final +@typing.final class DisposeResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -437,13 +1101,12 @@ class DisposeResponse(google.protobuf.message.Message): *, async_id: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_async_id", b"_async_id", "async_id", b"async_id"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_async_id", b"_async_id", "async_id", b"async_id"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_async_id", b"_async_id"]) -> typing_extensions.Literal["async_id"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___DisposeResponse = DisposeResponse -@typing_extensions.final +@typing.final class DisposeCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -452,13 +1115,14 @@ class DisposeCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___DisposeCallback = DisposeCallback -@typing_extensions.final +@typing.final class LogRecord(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -478,25 +1142,19 @@ class LogRecord(google.protobuf.message.Message): def __init__( self, *, - level: global___LogLevel.ValueType = ..., - target: builtins.str = ..., + level: global___LogLevel.ValueType | None = ..., + target: builtins.str | None = ..., module_path: builtins.str | None = ..., file: builtins.str | None = ..., line: builtins.int | None = ..., - message: builtins.str = ..., + message: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_file", b"_file", "_line", b"_line", "_module_path", b"_module_path", "file", b"file", "line", b"line", "module_path", b"module_path"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_file", b"_file", "_line", b"_line", "_module_path", b"_module_path", "file", b"file", "level", b"level", "line", b"line", "message", b"message", "module_path", b"module_path", "target", b"target"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_file", b"_file"]) -> typing_extensions.Literal["file"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_line", b"_line"]) -> typing_extensions.Literal["line"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_module_path", b"_module_path"]) -> typing_extensions.Literal["module_path"] | None: ... + def HasField(self, field_name: typing.Literal["file", b"file", "level", b"level", "line", b"line", "message", b"message", "module_path", b"module_path", "target", b"target"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["file", b"file", "level", b"level", "line", b"line", "message", b"message", "module_path", b"module_path", "target", b"target"]) -> None: ... global___LogRecord = LogRecord -@typing_extensions.final +@typing.final class LogBatch(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -508,11 +1166,11 @@ class LogBatch(google.protobuf.message.Message): *, records: collections.abc.Iterable[global___LogRecord] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["records", b"records"]) -> None: ... + def ClearField(self, field_name: typing.Literal["records", b"records"]) -> None: ... global___LogBatch = LogBatch -@typing_extensions.final +@typing.final class Panic(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -521,8 +1179,9 @@ class Panic(google.protobuf.message.Message): def __init__( self, *, - message: builtins.str = ..., + message: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["message", b"message"]) -> None: ... + def HasField(self, field_name: typing.Literal["message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["message", b"message"]) -> None: ... global___Panic = Panic diff --git a/livekit-rtc/livekit/rtc/_proto/handle_pb2.py b/livekit-rtc/livekit/rtc/_proto/handle_pb2.py index f3230797..2ae2db78 100644 --- a/livekit-rtc/livekit/rtc/_proto/handle_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/handle_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: handle.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0chandle.proto\x12\rlivekit.proto\"\x1c\n\x0e\x46\x66iOwnedHandle\x12\n\n\x02id\x18\x01 \x01(\x04\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0chandle.proto\x12\rlivekit.proto\"\x1c\n\x0e\x46\x66iOwnedHandle\x12\n\n\x02id\x18\x01 \x02(\x04\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) diff --git a/livekit-rtc/livekit/rtc/_proto/handle_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/handle_pb2.pyi index 5b525ca9..d433a6c2 100644 --- a/livekit-rtc/livekit/rtc/_proto/handle_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/handle_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,19 +15,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import google.protobuf.descriptor import google.protobuf.message -import sys - -if sys.version_info >= (3, 8): - import typing as typing_extensions -else: - import typing_extensions +import typing DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -@typing_extensions.final +@typing.final class FfiOwnedHandle(google.protobuf.message.Message): """# Safety The foreign language is responsable for disposing handles @@ -47,8 +43,9 @@ class FfiOwnedHandle(google.protobuf.message.Message): def __init__( self, *, - id: builtins.int = ..., + id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["id", b"id"]) -> None: ... + def HasField(self, field_name: typing.Literal["id", b"id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["id", b"id"]) -> None: ... global___FfiOwnedHandle = FfiOwnedHandle diff --git a/livekit-rtc/livekit/rtc/_proto/participant_pb2.py b/livekit-rtc/livekit/rtc/_proto/participant_pb2.py index 0141671d..b7fb7742 100644 --- a/livekit-rtc/livekit/rtc/_proto/participant_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/participant_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: participant.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,9 +13,10 @@ from . import handle_pb2 as handle__pb2 +from . import track_pb2 as track__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11participant.proto\x12\rlivekit.proto\x1a\x0chandle.proto\"P\n\x0fParticipantInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08identity\x18\x03 \x01(\t\x12\x10\n\x08metadata\x18\x04 \x01(\t\"o\n\x10OwnedParticipant\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.ParticipantInfoB\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11participant.proto\x12\rlivekit.proto\x1a\x0chandle.proto\x1a\x0btrack.proto\"\x83\x04\n\x0fParticipantInfo\x12\x0b\n\x03sid\x18\x01 \x02(\t\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x10\n\x08identity\x18\x03 \x02(\t\x12.\n\x05state\x18\x04 \x02(\x0e\x32\x1f.livekit.proto.ParticipantState\x12\x10\n\x08metadata\x18\x05 \x02(\t\x12\x42\n\nattributes\x18\x06 \x03(\x0b\x32..livekit.proto.ParticipantInfo.AttributesEntry\x12,\n\x04kind\x18\x07 \x02(\x0e\x32\x1e.livekit.proto.ParticipantKind\x12:\n\x11\x64isconnect_reason\x18\x08 \x02(\x0e\x32\x1f.livekit.proto.DisconnectReason\x12\x11\n\tjoined_at\x18\t \x02(\x03\x12:\n\x0ckind_details\x18\n \x03(\x0e\x32$.livekit.proto.ParticipantKindDetail\x12\x38\n\npermission\x18\x0b \x01(\x0b\x32$.livekit.proto.ParticipantPermission\x12\x17\n\x0f\x63lient_protocol\x18\x0c \x02(\x05\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"o\n\x10OwnedParticipant\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.ParticipantInfo\"\x84\x02\n\x15ParticipantPermission\x12\x15\n\rcan_subscribe\x18\x01 \x02(\x08\x12\x13\n\x0b\x63\x61n_publish\x18\x02 \x02(\x08\x12\x18\n\x10\x63\x61n_publish_data\x18\x03 \x02(\x08\x12\x37\n\x13\x63\x61n_publish_sources\x18\t \x03(\x0e\x32\x1a.livekit.proto.TrackSource\x12\x0e\n\x06hidden\x18\x07 \x02(\x08\x12\x1b\n\x13\x63\x61n_update_metadata\x18\n \x02(\x08\x12\x1d\n\x15\x63\x61n_subscribe_metrics\x18\x0c \x02(\x08\x12 \n\x18\x63\x61n_manage_agent_session\x18\r \x02(\x08*\x91\x01\n\x10ParticipantState\x12\x1d\n\x19PARTICIPANT_STATE_JOINING\x10\x00\x12\x1c\n\x18PARTICIPANT_STATE_JOINED\x10\x01\x12\x1c\n\x18PARTICIPANT_STATE_ACTIVE\x10\x02\x12\"\n\x1ePARTICIPANT_STATE_DISCONNECTED\x10\x03*\xde\x01\n\x0fParticipantKind\x12\x1d\n\x19PARTICIPANT_KIND_STANDARD\x10\x00\x12\x1c\n\x18PARTICIPANT_KIND_INGRESS\x10\x01\x12\x1b\n\x17PARTICIPANT_KIND_EGRESS\x10\x02\x12\x18\n\x14PARTICIPANT_KIND_SIP\x10\x03\x12\x1a\n\x16PARTICIPANT_KIND_AGENT\x10\x04\x12\x1e\n\x1aPARTICIPANT_KIND_CONNECTOR\x10\x05\x12\x1b\n\x17PARTICIPANT_KIND_BRIDGE\x10\x06*\x96\x02\n\x15ParticipantKindDetail\x12\'\n#PARTICIPANT_KIND_DETAIL_CLOUD_AGENT\x10\x00\x12%\n!PARTICIPANT_KIND_DETAIL_FORWARDED\x10\x01\x12.\n*PARTICIPANT_KIND_DETAIL_CONNECTOR_WHATSAPP\x10\x02\x12,\n(PARTICIPANT_KIND_DETAIL_CONNECTOR_TWILIO\x10\x03\x12\'\n#PARTICIPANT_KIND_DETAIL_BRIDGE_RTSP\x10\x04\x12&\n\"PARTICIPANT_KIND_DETAIL_SIMULATION\x10\x05*\xe8\x02\n\x10\x44isconnectReason\x12\x12\n\x0eUNKNOWN_REASON\x10\x00\x12\x14\n\x10\x43LIENT_INITIATED\x10\x01\x12\x16\n\x12\x44UPLICATE_IDENTITY\x10\x02\x12\x13\n\x0fSERVER_SHUTDOWN\x10\x03\x12\x17\n\x13PARTICIPANT_REMOVED\x10\x04\x12\x10\n\x0cROOM_DELETED\x10\x05\x12\x12\n\x0eSTATE_MISMATCH\x10\x06\x12\x10\n\x0cJOIN_FAILURE\x10\x07\x12\r\n\tMIGRATION\x10\x08\x12\x10\n\x0cSIGNAL_CLOSE\x10\t\x12\x0f\n\x0bROOM_CLOSED\x10\n\x12\x14\n\x10USER_UNAVAILABLE\x10\x0b\x12\x11\n\rUSER_REJECTED\x10\x0c\x12\x15\n\x11SIP_TRUNK_FAILURE\x10\r\x12\x16\n\x12\x43ONNECTION_TIMEOUT\x10\x0e\x12\x11\n\rMEDIA_FAILURE\x10\x0f\x12\x0f\n\x0b\x41GENT_ERROR\x10\x10\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -23,8 +24,22 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_PARTICIPANTINFO']._serialized_start=50 - _globals['_PARTICIPANTINFO']._serialized_end=130 - _globals['_OWNEDPARTICIPANT']._serialized_start=132 - _globals['_OWNEDPARTICIPANT']._serialized_end=243 + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._options = None + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_PARTICIPANTSTATE']._serialized_start=958 + _globals['_PARTICIPANTSTATE']._serialized_end=1103 + _globals['_PARTICIPANTKIND']._serialized_start=1106 + _globals['_PARTICIPANTKIND']._serialized_end=1328 + _globals['_PARTICIPANTKINDDETAIL']._serialized_start=1331 + _globals['_PARTICIPANTKINDDETAIL']._serialized_end=1609 + _globals['_DISCONNECTREASON']._serialized_start=1612 + _globals['_DISCONNECTREASON']._serialized_end=1972 + _globals['_PARTICIPANTINFO']._serialized_start=64 + _globals['_PARTICIPANTINFO']._serialized_end=579 + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_start=530 + _globals['_PARTICIPANTINFO_ATTRIBUTESENTRY']._serialized_end=579 + _globals['_OWNEDPARTICIPANT']._serialized_start=581 + _globals['_OWNEDPARTICIPANT']._serialized_end=692 + _globals['_PARTICIPANTPERMISSION']._serialized_start=695 + _globals['_PARTICIPANTPERMISSION']._serialized_end=955 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/participant_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/participant_pb2.pyi index 0b30d59e..1e34abda 100644 --- a/livekit-rtc/livekit/rtc/_proto/participant_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/participant_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,44 +15,234 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins +import collections.abc import google.protobuf.descriptor +import google.protobuf.internal.containers +import google.protobuf.internal.enum_type_wrapper import google.protobuf.message from . import handle_pb2 import sys +from . import track_pb2 +import typing -if sys.version_info >= (3, 8): +if sys.version_info >= (3, 10): import typing as typing_extensions else: import typing_extensions DESCRIPTOR: google.protobuf.descriptor.FileDescriptor -@typing_extensions.final +class _ParticipantState: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ParticipantStateEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ParticipantState.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PARTICIPANT_STATE_JOINING: _ParticipantState.ValueType # 0 + PARTICIPANT_STATE_JOINED: _ParticipantState.ValueType # 1 + PARTICIPANT_STATE_ACTIVE: _ParticipantState.ValueType # 2 + PARTICIPANT_STATE_DISCONNECTED: _ParticipantState.ValueType # 3 + +class ParticipantState(_ParticipantState, metaclass=_ParticipantStateEnumTypeWrapper): ... + +PARTICIPANT_STATE_JOINING: ParticipantState.ValueType # 0 +PARTICIPANT_STATE_JOINED: ParticipantState.ValueType # 1 +PARTICIPANT_STATE_ACTIVE: ParticipantState.ValueType # 2 +PARTICIPANT_STATE_DISCONNECTED: ParticipantState.ValueType # 3 +global___ParticipantState = ParticipantState + +class _ParticipantKind: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ParticipantKindEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ParticipantKind.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PARTICIPANT_KIND_STANDARD: _ParticipantKind.ValueType # 0 + PARTICIPANT_KIND_INGRESS: _ParticipantKind.ValueType # 1 + PARTICIPANT_KIND_EGRESS: _ParticipantKind.ValueType # 2 + PARTICIPANT_KIND_SIP: _ParticipantKind.ValueType # 3 + PARTICIPANT_KIND_AGENT: _ParticipantKind.ValueType # 4 + PARTICIPANT_KIND_CONNECTOR: _ParticipantKind.ValueType # 5 + PARTICIPANT_KIND_BRIDGE: _ParticipantKind.ValueType # 6 + +class ParticipantKind(_ParticipantKind, metaclass=_ParticipantKindEnumTypeWrapper): ... + +PARTICIPANT_KIND_STANDARD: ParticipantKind.ValueType # 0 +PARTICIPANT_KIND_INGRESS: ParticipantKind.ValueType # 1 +PARTICIPANT_KIND_EGRESS: ParticipantKind.ValueType # 2 +PARTICIPANT_KIND_SIP: ParticipantKind.ValueType # 3 +PARTICIPANT_KIND_AGENT: ParticipantKind.ValueType # 4 +PARTICIPANT_KIND_CONNECTOR: ParticipantKind.ValueType # 5 +PARTICIPANT_KIND_BRIDGE: ParticipantKind.ValueType # 6 +global___ParticipantKind = ParticipantKind + +class _ParticipantKindDetail: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _ParticipantKindDetailEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_ParticipantKindDetail.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + PARTICIPANT_KIND_DETAIL_CLOUD_AGENT: _ParticipantKindDetail.ValueType # 0 + PARTICIPANT_KIND_DETAIL_FORWARDED: _ParticipantKindDetail.ValueType # 1 + PARTICIPANT_KIND_DETAIL_CONNECTOR_WHATSAPP: _ParticipantKindDetail.ValueType # 2 + PARTICIPANT_KIND_DETAIL_CONNECTOR_TWILIO: _ParticipantKindDetail.ValueType # 3 + PARTICIPANT_KIND_DETAIL_BRIDGE_RTSP: _ParticipantKindDetail.ValueType # 4 + PARTICIPANT_KIND_DETAIL_SIMULATION: _ParticipantKindDetail.ValueType # 5 + +class ParticipantKindDetail(_ParticipantKindDetail, metaclass=_ParticipantKindDetailEnumTypeWrapper): ... + +PARTICIPANT_KIND_DETAIL_CLOUD_AGENT: ParticipantKindDetail.ValueType # 0 +PARTICIPANT_KIND_DETAIL_FORWARDED: ParticipantKindDetail.ValueType # 1 +PARTICIPANT_KIND_DETAIL_CONNECTOR_WHATSAPP: ParticipantKindDetail.ValueType # 2 +PARTICIPANT_KIND_DETAIL_CONNECTOR_TWILIO: ParticipantKindDetail.ValueType # 3 +PARTICIPANT_KIND_DETAIL_BRIDGE_RTSP: ParticipantKindDetail.ValueType # 4 +PARTICIPANT_KIND_DETAIL_SIMULATION: ParticipantKindDetail.ValueType # 5 +global___ParticipantKindDetail = ParticipantKindDetail + +class _DisconnectReason: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DisconnectReasonEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DisconnectReason.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNKNOWN_REASON: _DisconnectReason.ValueType # 0 + CLIENT_INITIATED: _DisconnectReason.ValueType # 1 + """the client initiated the disconnect""" + DUPLICATE_IDENTITY: _DisconnectReason.ValueType # 2 + """another participant with the same identity has joined the room""" + SERVER_SHUTDOWN: _DisconnectReason.ValueType # 3 + """the server instance is shutting down""" + PARTICIPANT_REMOVED: _DisconnectReason.ValueType # 4 + """RoomService.RemoveParticipant was called""" + ROOM_DELETED: _DisconnectReason.ValueType # 5 + """RoomService.DeleteRoom was called""" + STATE_MISMATCH: _DisconnectReason.ValueType # 6 + """the client is attempting to resume a session, but server is not aware of it""" + JOIN_FAILURE: _DisconnectReason.ValueType # 7 + """client was unable to connect fully""" + MIGRATION: _DisconnectReason.ValueType # 8 + """Cloud-only, the server requested Participant to migrate the connection elsewhere""" + SIGNAL_CLOSE: _DisconnectReason.ValueType # 9 + """the signal websocket was closed unexpectedly""" + ROOM_CLOSED: _DisconnectReason.ValueType # 10 + """the room was closed, due to all Standard and Ingress participants having left""" + USER_UNAVAILABLE: _DisconnectReason.ValueType # 11 + """SIP callee did not respond in time""" + USER_REJECTED: _DisconnectReason.ValueType # 12 + """SIP callee rejected the call (busy)""" + SIP_TRUNK_FAILURE: _DisconnectReason.ValueType # 13 + """SIP protocol failure or unexpected response""" + CONNECTION_TIMEOUT: _DisconnectReason.ValueType # 14 + MEDIA_FAILURE: _DisconnectReason.ValueType # 15 + AGENT_ERROR: _DisconnectReason.ValueType # 16 + +class DisconnectReason(_DisconnectReason, metaclass=_DisconnectReasonEnumTypeWrapper): ... + +UNKNOWN_REASON: DisconnectReason.ValueType # 0 +CLIENT_INITIATED: DisconnectReason.ValueType # 1 +"""the client initiated the disconnect""" +DUPLICATE_IDENTITY: DisconnectReason.ValueType # 2 +"""another participant with the same identity has joined the room""" +SERVER_SHUTDOWN: DisconnectReason.ValueType # 3 +"""the server instance is shutting down""" +PARTICIPANT_REMOVED: DisconnectReason.ValueType # 4 +"""RoomService.RemoveParticipant was called""" +ROOM_DELETED: DisconnectReason.ValueType # 5 +"""RoomService.DeleteRoom was called""" +STATE_MISMATCH: DisconnectReason.ValueType # 6 +"""the client is attempting to resume a session, but server is not aware of it""" +JOIN_FAILURE: DisconnectReason.ValueType # 7 +"""client was unable to connect fully""" +MIGRATION: DisconnectReason.ValueType # 8 +"""Cloud-only, the server requested Participant to migrate the connection elsewhere""" +SIGNAL_CLOSE: DisconnectReason.ValueType # 9 +"""the signal websocket was closed unexpectedly""" +ROOM_CLOSED: DisconnectReason.ValueType # 10 +"""the room was closed, due to all Standard and Ingress participants having left""" +USER_UNAVAILABLE: DisconnectReason.ValueType # 11 +"""SIP callee did not respond in time""" +USER_REJECTED: DisconnectReason.ValueType # 12 +"""SIP callee rejected the call (busy)""" +SIP_TRUNK_FAILURE: DisconnectReason.ValueType # 13 +"""SIP protocol failure or unexpected response""" +CONNECTION_TIMEOUT: DisconnectReason.ValueType # 14 +MEDIA_FAILURE: DisconnectReason.ValueType # 15 +AGENT_ERROR: DisconnectReason.ValueType # 16 +global___DisconnectReason = DisconnectReason + +@typing.final class ParticipantInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + SID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int IDENTITY_FIELD_NUMBER: builtins.int + STATE_FIELD_NUMBER: builtins.int METADATA_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + KIND_FIELD_NUMBER: builtins.int + DISCONNECT_REASON_FIELD_NUMBER: builtins.int + JOINED_AT_FIELD_NUMBER: builtins.int + KIND_DETAILS_FIELD_NUMBER: builtins.int + PERMISSION_FIELD_NUMBER: builtins.int + CLIENT_PROTOCOL_FIELD_NUMBER: builtins.int sid: builtins.str name: builtins.str identity: builtins.str + state: global___ParticipantState.ValueType metadata: builtins.str + kind: global___ParticipantKind.ValueType + disconnect_reason: global___DisconnectReason.ValueType + joined_at: builtins.int + """ms timestamp of when the participant joined the room, maps to joined_at_ms in livekit_models""" + client_protocol: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: ... + @property + def kind_details(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___ParticipantKindDetail.ValueType]: ... + @property + def permission(self) -> global___ParticipantPermission: ... def __init__( self, *, - sid: builtins.str = ..., - name: builtins.str = ..., - identity: builtins.str = ..., - metadata: builtins.str = ..., + sid: builtins.str | None = ..., + name: builtins.str | None = ..., + identity: builtins.str | None = ..., + state: global___ParticipantState.ValueType | None = ..., + metadata: builtins.str | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + kind: global___ParticipantKind.ValueType | None = ..., + disconnect_reason: global___DisconnectReason.ValueType | None = ..., + joined_at: builtins.int | None = ..., + kind_details: collections.abc.Iterable[global___ParticipantKindDetail.ValueType] | None = ..., + permission: global___ParticipantPermission | None = ..., + client_protocol: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["identity", b"identity", "metadata", b"metadata", "name", b"name", "sid", b"sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["client_protocol", b"client_protocol", "disconnect_reason", b"disconnect_reason", "identity", b"identity", "joined_at", b"joined_at", "kind", b"kind", "metadata", b"metadata", "name", b"name", "permission", b"permission", "sid", b"sid", "state", b"state"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "client_protocol", b"client_protocol", "disconnect_reason", b"disconnect_reason", "identity", b"identity", "joined_at", b"joined_at", "kind", b"kind", "kind_details", b"kind_details", "metadata", b"metadata", "name", b"name", "permission", b"permission", "sid", b"sid", "state", b"state"]) -> None: ... global___ParticipantInfo = ParticipantInfo -@typing_extensions.final +@typing.final class OwnedParticipant(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -68,7 +258,56 @@ class OwnedParticipant(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___ParticipantInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedParticipant = OwnedParticipant + +@typing.final +class ParticipantPermission(google.protobuf.message.Message): + """copied from livekit-protocol/protocol/protobufs/livekit_models.proto and removed deprecated fields""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CAN_SUBSCRIBE_FIELD_NUMBER: builtins.int + CAN_PUBLISH_FIELD_NUMBER: builtins.int + CAN_PUBLISH_DATA_FIELD_NUMBER: builtins.int + CAN_PUBLISH_SOURCES_FIELD_NUMBER: builtins.int + HIDDEN_FIELD_NUMBER: builtins.int + CAN_UPDATE_METADATA_FIELD_NUMBER: builtins.int + CAN_SUBSCRIBE_METRICS_FIELD_NUMBER: builtins.int + CAN_MANAGE_AGENT_SESSION_FIELD_NUMBER: builtins.int + can_subscribe: builtins.bool + """allow participant to subscribe to other tracks in the room""" + can_publish: builtins.bool + """allow participant to publish new tracks to room""" + can_publish_data: builtins.bool + """allow participant to publish data""" + hidden: builtins.bool + """indicates that it's hidden to others""" + can_update_metadata: builtins.bool + """indicates that participant can update own metadata and attributes""" + can_subscribe_metrics: builtins.bool + """if a participant can subscribe to metrics""" + can_manage_agent_session: builtins.bool + """if a participant can manage an agent session via RemoteSession (control and access state)""" + @property + def can_publish_sources(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[track_pb2.TrackSource.ValueType]: + """sources that are allowed to be published""" + + def __init__( + self, + *, + can_subscribe: builtins.bool | None = ..., + can_publish: builtins.bool | None = ..., + can_publish_data: builtins.bool | None = ..., + can_publish_sources: collections.abc.Iterable[track_pb2.TrackSource.ValueType] | None = ..., + hidden: builtins.bool | None = ..., + can_update_metadata: builtins.bool | None = ..., + can_subscribe_metrics: builtins.bool | None = ..., + can_manage_agent_session: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["can_manage_agent_session", b"can_manage_agent_session", "can_publish", b"can_publish", "can_publish_data", b"can_publish_data", "can_subscribe", b"can_subscribe", "can_subscribe_metrics", b"can_subscribe_metrics", "can_update_metadata", b"can_update_metadata", "hidden", b"hidden"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["can_manage_agent_session", b"can_manage_agent_session", "can_publish", b"can_publish", "can_publish_data", b"can_publish_data", "can_publish_sources", b"can_publish_sources", "can_subscribe", b"can_subscribe", "can_subscribe_metrics", b"can_subscribe_metrics", "can_update_metadata", b"can_update_metadata", "hidden", b"hidden"]) -> None: ... + +global___ParticipantPermission = ParticipantPermission diff --git a/livekit-rtc/livekit/rtc/_proto/room_pb2.py b/livekit-rtc/livekit/rtc/_proto/room_pb2.py index b320a2f4..e5041ba2 100644 --- a/livekit-rtc/livekit/rtc/_proto/room_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/room_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: room.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -18,9 +18,11 @@ from . import track_pb2 as track__pb2 from . import video_frame_pb2 as video__frame__pb2 from . import stats_pb2 as stats__pb2 +from . import data_stream_pb2 as data__stream__pb2 +from . import data_track_pb2 as data__track__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nroom.proto\x12\rlivekit.proto\x1a\ne2ee.proto\x1a\x0chandle.proto\x1a\x11participant.proto\x1a\x0btrack.proto\x1a\x11video_frame.proto\x1a\x0bstats.proto\"Y\n\x0e\x43onnectRequest\x12\x0b\n\x03url\x18\x01 \x01(\t\x12\r\n\x05token\x18\x02 \x01(\t\x12+\n\x07options\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.RoomOptions\"#\n\x0f\x43onnectResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"\xfd\x02\n\x0f\x43onnectCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x12&\n\x04room\x18\x03 \x01(\x0b\x32\x18.livekit.proto.OwnedRoom\x12:\n\x11local_participant\x18\x04 \x01(\x0b\x32\x1f.livekit.proto.OwnedParticipant\x12J\n\x0cparticipants\x18\x05 \x03(\x0b\x32\x34.livekit.proto.ConnectCallback.ParticipantWithTracks\x1a\x89\x01\n\x15ParticipantWithTracks\x12\x34\n\x0bparticipant\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedParticipant\x12:\n\x0cpublications\x18\x02 \x03(\x0b\x32$.livekit.proto.OwnedTrackPublicationB\x08\n\x06_error\"(\n\x11\x44isconnectRequest\x12\x13\n\x0broom_handle\x18\x01 \x01(\x04\"&\n\x12\x44isconnectResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"&\n\x12\x44isconnectCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"\x82\x01\n\x13PublishTrackRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x14\n\x0ctrack_handle\x18\x02 \x01(\x04\x12\x33\n\x07options\x18\x03 \x01(\x0b\x32\".livekit.proto.TrackPublishOptions\"(\n\x14PublishTrackResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"\x81\x01\n\x14PublishTrackCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x39\n\x0bpublication\x18\x03 \x01(\x0b\x32$.livekit.proto.OwnedTrackPublicationB\x08\n\x06_error\"g\n\x15UnpublishTrackRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x19\n\x11stop_on_unpublish\x18\x03 \x01(\x08\"*\n\x16UnpublishTrackResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"H\n\x16UnpublishTrackCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\xbf\x01\n\x12PublishDataRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x10\n\x08\x64\x61ta_ptr\x18\x02 \x01(\x04\x12\x10\n\x08\x64\x61ta_len\x18\x03 \x01(\x04\x12+\n\x04kind\x18\x04 \x01(\x0e\x32\x1d.livekit.proto.DataPacketKind\x12\x18\n\x10\x64\x65stination_sids\x18\x05 \x03(\t\x12\x12\n\x05topic\x18\x06 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_topic\"\'\n\x13PublishDataResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"E\n\x13PublishDataCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"\xb8\x01\n\x1bPublishTranscriptionRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x1c\n\x14participant_identity\x18\x02 \x01(\t\x12\x10\n\x08track_id\x18\x03 \x01(\t\x12\x35\n\x08segments\x18\x04 \x03(\x0b\x32#.livekit.proto.TranscriptionSegment\x12\x10\n\x08language\x18\x05 \x01(\t\"0\n\x1cPublishTranscriptionResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"N\n\x1cPublishTranscriptionCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_error\"P\n\x1aUpdateLocalMetadataRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x10\n\x08metadata\x18\x02 \x01(\t\"/\n\x1bUpdateLocalMetadataResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"/\n\x1bUpdateLocalMetadataCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"H\n\x16UpdateLocalNameRequest\x12 \n\x18local_participant_handle\x18\x01 \x01(\x04\x12\x0c\n\x04name\x18\x02 \x01(\t\"+\n\x17UpdateLocalNameResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"+\n\x17UpdateLocalNameCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"E\n\x14SetSubscribedRequest\x12\x11\n\tsubscribe\x18\x01 \x01(\x08\x12\x1a\n\x12publication_handle\x18\x02 \x01(\x04\"\x17\n\x15SetSubscribedResponse\"-\n\x16GetSessionStatsRequest\x12\x13\n\x0broom_handle\x18\x01 \x01(\x04\"+\n\x17GetSessionStatsResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"\xae\x01\n\x17GetSessionStatsCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x12\x30\n\x0fpublisher_stats\x18\x03 \x03(\x0b\x32\x17.livekit.proto.RtcStats\x12\x31\n\x10subscriber_stats\x18\x04 \x03(\x0b\x32\x17.livekit.proto.RtcStatsB\x08\n\x06_error\";\n\rVideoEncoding\x12\x13\n\x0bmax_bitrate\x18\x01 \x01(\x04\x12\x15\n\rmax_framerate\x18\x02 \x01(\x01\"$\n\rAudioEncoding\x12\x13\n\x0bmax_bitrate\x18\x01 \x01(\x04\"\x8a\x02\n\x13TrackPublishOptions\x12\x34\n\x0evideo_encoding\x18\x01 \x01(\x0b\x32\x1c.livekit.proto.VideoEncoding\x12\x34\n\x0e\x61udio_encoding\x18\x02 \x01(\x0b\x32\x1c.livekit.proto.AudioEncoding\x12.\n\x0bvideo_codec\x18\x03 \x01(\x0e\x32\x19.livekit.proto.VideoCodec\x12\x0b\n\x03\x64tx\x18\x04 \x01(\x08\x12\x0b\n\x03red\x18\x05 \x01(\x08\x12\x11\n\tsimulcast\x18\x06 \x01(\x08\x12*\n\x06source\x18\x07 \x01(\x0e\x32\x1a.livekit.proto.TrackSource\"=\n\tIceServer\x12\x0c\n\x04urls\x18\x01 \x03(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\"\x84\x02\n\tRtcConfig\x12@\n\x12ice_transport_type\x18\x01 \x01(\x0e\x32\x1f.livekit.proto.IceTransportTypeH\x00\x88\x01\x01\x12P\n\x1a\x63ontinual_gathering_policy\x18\x02 \x01(\x0e\x32\'.livekit.proto.ContinualGatheringPolicyH\x01\x88\x01\x01\x12-\n\x0bice_servers\x18\x03 \x03(\x0b\x32\x18.livekit.proto.IceServerB\x15\n\x13_ice_transport_typeB\x1d\n\x1b_continual_gathering_policy\"\xe0\x01\n\x0bRoomOptions\x12\x16\n\x0e\x61uto_subscribe\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x64\x61ptive_stream\x18\x02 \x01(\x08\x12\x10\n\x08\x64ynacast\x18\x03 \x01(\x08\x12-\n\x04\x65\x32\x65\x65\x18\x04 \x01(\x0b\x32\x1a.livekit.proto.E2eeOptionsH\x00\x88\x01\x01\x12\x31\n\nrtc_config\x18\x05 \x01(\x0b\x32\x18.livekit.proto.RtcConfigH\x01\x88\x01\x01\x12\x14\n\x0cjoin_retries\x18\x06 \x01(\rB\x07\n\x05_e2eeB\r\n\x0b_rtc_config\"e\n\x14TranscriptionSegment\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0c\n\x04text\x18\x02 \x01(\t\x12\x12\n\nstart_time\x18\x03 \x01(\x04\x12\x10\n\x08\x65nd_time\x18\x04 \x01(\x04\x12\r\n\x05\x66inal\x18\x05 \x01(\x08\"0\n\nBufferInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x01(\x04\x12\x10\n\x08\x64\x61ta_len\x18\x02 \x01(\x04\"e\n\x0bOwnedBuffer\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\'\n\x04\x64\x61ta\x18\x02 \x01(\x0b\x32\x19.livekit.proto.BufferInfo\"\x80\x0c\n\tRoomEvent\x12\x13\n\x0broom_handle\x18\x01 \x01(\x04\x12\x44\n\x15participant_connected\x18\x02 \x01(\x0b\x32#.livekit.proto.ParticipantConnectedH\x00\x12J\n\x18participant_disconnected\x18\x03 \x01(\x0b\x32&.livekit.proto.ParticipantDisconnectedH\x00\x12\x43\n\x15local_track_published\x18\x04 \x01(\x0b\x32\".livekit.proto.LocalTrackPublishedH\x00\x12G\n\x17local_track_unpublished\x18\x05 \x01(\x0b\x32$.livekit.proto.LocalTrackUnpublishedH\x00\x12\x38\n\x0ftrack_published\x18\x06 \x01(\x0b\x32\x1d.livekit.proto.TrackPublishedH\x00\x12<\n\x11track_unpublished\x18\x07 \x01(\x0b\x32\x1f.livekit.proto.TrackUnpublishedH\x00\x12:\n\x10track_subscribed\x18\x08 \x01(\x0b\x32\x1e.livekit.proto.TrackSubscribedH\x00\x12>\n\x12track_unsubscribed\x18\t \x01(\x0b\x32 .livekit.proto.TrackUnsubscribedH\x00\x12K\n\x19track_subscription_failed\x18\n \x01(\x0b\x32&.livekit.proto.TrackSubscriptionFailedH\x00\x12\x30\n\x0btrack_muted\x18\x0b \x01(\x0b\x32\x19.livekit.proto.TrackMutedH\x00\x12\x34\n\rtrack_unmuted\x18\x0c \x01(\x0b\x32\x1b.livekit.proto.TrackUnmutedH\x00\x12G\n\x17\x61\x63tive_speakers_changed\x18\r \x01(\x0b\x32$.livekit.proto.ActiveSpeakersChangedH\x00\x12\x43\n\x15room_metadata_changed\x18\x0e \x01(\x0b\x32\".livekit.proto.RoomMetadataChangedH\x00\x12Q\n\x1cparticipant_metadata_changed\x18\x0f \x01(\x0b\x32).livekit.proto.ParticipantMetadataChangedH\x00\x12I\n\x18participant_name_changed\x18\x10 \x01(\x0b\x32%.livekit.proto.ParticipantNameChangedH\x00\x12M\n\x1a\x63onnection_quality_changed\x18\x11 \x01(\x0b\x32\'.livekit.proto.ConnectionQualityChangedH\x00\x12I\n\x18\x63onnection_state_changed\x18\x13 \x01(\x0b\x32%.livekit.proto.ConnectionStateChangedH\x00\x12\x33\n\x0c\x64isconnected\x18\x15 \x01(\x0b\x32\x1b.livekit.proto.DisconnectedH\x00\x12\x33\n\x0creconnecting\x18\x16 \x01(\x0b\x32\x1b.livekit.proto.ReconnectingH\x00\x12\x31\n\x0breconnected\x18\x17 \x01(\x0b\x32\x1a.livekit.proto.ReconnectedH\x00\x12=\n\x12\x65\x32\x65\x65_state_changed\x18\x18 \x01(\x0b\x32\x1f.livekit.proto.E2eeStateChangedH\x00\x12%\n\x03\x65os\x18\x19 \x01(\x0b\x32\x16.livekit.proto.RoomEOSH\x00\x12\x41\n\x14\x64\x61ta_packet_received\x18\x1a \x01(\x0b\x32!.livekit.proto.DataPacketReceivedH\x00\x42\t\n\x07message\"7\n\x08RoomInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08metadata\x18\x03 \x01(\t\"a\n\tOwnedRoom\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12%\n\x04info\x18\x02 \x01(\x0b\x32\x17.livekit.proto.RoomInfo\"E\n\x14ParticipantConnected\x12-\n\x04info\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedParticipant\"2\n\x17ParticipantDisconnected\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\"(\n\x13LocalTrackPublished\x12\x11\n\ttrack_sid\x18\x01 \x01(\t\"0\n\x15LocalTrackUnpublished\x12\x17\n\x0fpublication_sid\x18\x01 \x01(\t\"d\n\x0eTrackPublished\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x39\n\x0bpublication\x18\x02 \x01(\x0b\x32$.livekit.proto.OwnedTrackPublication\"D\n\x10TrackUnpublished\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x17\n\x0fpublication_sid\x18\x02 \x01(\t\"T\n\x0fTrackSubscribed\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12(\n\x05track\x18\x02 \x01(\x0b\x32\x19.livekit.proto.OwnedTrack\"?\n\x11TrackUnsubscribed\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\"T\n\x17TrackSubscriptionFailed\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\r\n\x05\x65rror\x18\x03 \x01(\t\"8\n\nTrackMuted\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\":\n\x0cTrackUnmuted\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\"Z\n\x10\x45\x32\x65\x65StateChanged\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12-\n\x05state\x18\x02 \x01(\x0e\x32\x1e.livekit.proto.EncryptionState\"1\n\x15\x41\x63tiveSpeakersChanged\x12\x18\n\x10participant_sids\x18\x01 \x03(\t\"\'\n\x13RoomMetadataChanged\x12\x10\n\x08metadata\x18\x01 \x01(\t\"G\n\x1aParticipantMetadataChanged\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x10\n\x08metadata\x18\x02 \x01(\t\"?\n\x16ParticipantNameChanged\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\"f\n\x18\x43onnectionQualityChanged\x12\x17\n\x0fparticipant_sid\x18\x01 \x01(\t\x12\x31\n\x07quality\x18\x02 \x01(\x0e\x32 .livekit.proto.ConnectionQuality\"T\n\nUserPacket\x12(\n\x04\x64\x61ta\x18\x01 \x01(\x0b\x32\x1a.livekit.proto.OwnedBuffer\x12\x12\n\x05topic\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_topic\"5\n\x07SipDTMF\x12\x0c\n\x04\x63ode\x18\x01 \x01(\r\x12\x12\n\x05\x64igit\x18\x02 \x01(\tH\x00\x88\x01\x01\x42\x08\n\x06_digit\"\xf5\x01\n\x12\x44\x61taPacketReceived\x12+\n\x04kind\x18\x01 \x01(\x0e\x32\x1d.livekit.proto.DataPacketKind\x12\x1c\n\x14participant_identity\x18\x02 \x01(\t\x12 \n\x0fparticipant_sid\x18\x03 \x01(\tB\x02\x18\x01H\x01\x88\x01\x01\x12)\n\x04user\x18\x04 \x01(\x0b\x32\x19.livekit.proto.UserPacketH\x00\x12*\n\x08sip_dtmf\x18\x05 \x01(\x0b\x32\x16.livekit.proto.SipDTMFH\x00\x42\x07\n\x05valueB\x12\n\x10_participant_sid\"G\n\x16\x43onnectionStateChanged\x12-\n\x05state\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.ConnectionState\"\x0b\n\tConnected\"\x0e\n\x0c\x44isconnected\"\x0e\n\x0cReconnecting\"\r\n\x0bReconnected\"\t\n\x07RoomEOS*P\n\x10IceTransportType\x12\x13\n\x0fTRANSPORT_RELAY\x10\x00\x12\x14\n\x10TRANSPORT_NOHOST\x10\x01\x12\x11\n\rTRANSPORT_ALL\x10\x02*C\n\x18\x43ontinualGatheringPolicy\x12\x0f\n\x0bGATHER_ONCE\x10\x00\x12\x16\n\x12GATHER_CONTINUALLY\x10\x01*`\n\x11\x43onnectionQuality\x12\x10\n\x0cQUALITY_POOR\x10\x00\x12\x10\n\x0cQUALITY_GOOD\x10\x01\x12\x15\n\x11QUALITY_EXCELLENT\x10\x02\x12\x10\n\x0cQUALITY_LOST\x10\x03*S\n\x0f\x43onnectionState\x12\x15\n\x11\x43ONN_DISCONNECTED\x10\x00\x12\x12\n\x0e\x43ONN_CONNECTED\x10\x01\x12\x15\n\x11\x43ONN_RECONNECTING\x10\x02*3\n\x0e\x44\x61taPacketKind\x12\x0e\n\nKIND_LOSSY\x10\x00\x12\x11\n\rKIND_RELIABLE\x10\x01\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\nroom.proto\x12\rlivekit.proto\x1a\ne2ee.proto\x1a\x0chandle.proto\x1a\x11participant.proto\x1a\x0btrack.proto\x1a\x11video_frame.proto\x1a\x0bstats.proto\x1a\x11\x64\x61ta_stream.proto\x1a\x10\x64\x61ta_track.proto\"s\n\x0e\x43onnectRequest\x12\x0b\n\x03url\x18\x01 \x02(\t\x12\r\n\x05token\x18\x02 \x02(\t\x12+\n\x07options\x18\x03 \x02(\x0b\x32\x1a.livekit.proto.RoomOptions\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"#\n\x0f\x43onnectResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\xbf\x03\n\x0f\x43onnectCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x12\x37\n\x06result\x18\x03 \x01(\x0b\x32%.livekit.proto.ConnectCallback.ResultH\x00\x1a\x89\x01\n\x15ParticipantWithTracks\x12\x34\n\x0bparticipant\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedParticipant\x12:\n\x0cpublications\x18\x02 \x03(\x0b\x32$.livekit.proto.OwnedTrackPublication\x1a\xb8\x01\n\x06Result\x12&\n\x04room\x18\x01 \x02(\x0b\x32\x18.livekit.proto.OwnedRoom\x12:\n\x11local_participant\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.OwnedParticipant\x12J\n\x0cparticipants\x18\x03 \x03(\x0b\x32\x34.livekit.proto.ConnectCallback.ParticipantWithTracksB\t\n\x07message\"s\n\x11\x44isconnectRequest\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\x12/\n\x06reason\x18\x03 \x01(\x0e\x32\x1f.livekit.proto.DisconnectReason\"&\n\x12\x44isconnectResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"&\n\x12\x44isconnectCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x7f\n\x17SimulateScenarioRequest\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\x12\x35\n\x08scenario\x18\x02 \x02(\x0e\x32#.livekit.proto.SimulateScenarioKind\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\",\n\x18SimulateScenarioResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\";\n\x18SimulateScenarioCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x9c\x01\n\x13PublishTrackRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x14\n\x0ctrack_handle\x18\x02 \x02(\x04\x12\x33\n\x07options\x18\x03 \x02(\x0b\x32\".livekit.proto.TrackPublishOptions\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"(\n\x14PublishTrackResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x81\x01\n\x14PublishTrackCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x12;\n\x0bpublication\x18\x03 \x01(\x0b\x32$.livekit.proto.OwnedTrackPublicationH\x00\x42\t\n\x07message\"/\n\x18ReadyForRoomEventRequest\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\"\x1b\n\x19ReadyForRoomEventResponse\"\x81\x01\n\x15UnpublishTrackRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\x12\x19\n\x11stop_on_unpublish\x18\x03 \x02(\x08\x12\x18\n\x10request_async_id\x18\x04 \x01(\x04\"*\n\x16UnpublishTrackResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"9\n\x16UnpublishTrackCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\xd3\x01\n\x12PublishDataRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x61ta_ptr\x18\x02 \x02(\x04\x12\x10\n\x08\x64\x61ta_len\x18\x03 \x02(\x04\x12\x10\n\x08reliable\x18\x04 \x02(\x08\x12\x1c\n\x10\x64\x65stination_sids\x18\x05 \x03(\tB\x02\x18\x01\x12\r\n\x05topic\x18\x06 \x01(\t\x12\x1e\n\x16\x64\x65stination_identities\x18\x07 \x03(\t\x12\x18\n\x10request_async_id\x18\x08 \x01(\x04\"\'\n\x13PublishDataResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"6\n\x13PublishDataCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\xc0\x01\n\x1bPublishTranscriptionRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x1c\n\x14participant_identity\x18\x02 \x02(\t\x12\x10\n\x08track_id\x18\x03 \x02(\t\x12\x35\n\x08segments\x18\x04 \x03(\x0b\x32#.livekit.proto.TranscriptionSegment\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\"0\n\x1cPublishTranscriptionResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"?\n\x1cPublishTranscriptionCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x90\x01\n\x15PublishSipDtmfRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x0c\n\x04\x63ode\x18\x02 \x02(\r\x12\r\n\x05\x64igit\x18\x03 \x02(\t\x12\x1e\n\x16\x64\x65stination_identities\x18\x04 \x03(\t\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\"*\n\x16PublishSipDtmfResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"9\n\x16PublishSipDtmfCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"g\n\x17SetLocalMetadataRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x10\n\x08metadata\x18\x02 \x02(\t\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\",\n\x18SetLocalMetadataResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\";\n\x18SetLocalMetadataCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x9e\x01\n\x16SendChatMessageRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x0f\n\x07message\x18\x02 \x02(\t\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\x17\n\x0fsender_identity\x18\x04 \x01(\t\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\"\xd6\x01\n\x16\x45\x64itChatMessageRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x11\n\tedit_text\x18\x02 \x02(\t\x12\x34\n\x10original_message\x18\x03 \x02(\x0b\x32\x1a.livekit.proto.ChatMessage\x12\x1e\n\x16\x64\x65stination_identities\x18\x04 \x03(\t\x12\x17\n\x0fsender_identity\x18\x05 \x01(\t\x12\x18\n\x10request_async_id\x18\x06 \x01(\x04\"+\n\x17SendChatMessageResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"{\n\x17SendChatMessageCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x12\x32\n\x0c\x63hat_message\x18\x03 \x01(\x0b\x32\x1a.livekit.proto.ChatMessageH\x00\x42\t\n\x07message\"\x8b\x01\n\x19SetLocalAttributesRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x32\n\nattributes\x18\x02 \x03(\x0b\x32\x1e.livekit.proto.AttributesEntry\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"-\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t\".\n\x1aSetLocalAttributesResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"=\n\x1aSetLocalAttributesCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"_\n\x13SetLocalNameRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x18\n\x10request_async_id\x18\x03 \x01(\x04\"(\n\x14SetLocalNameResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"7\n\x14SetLocalNameCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"E\n\x14SetSubscribedRequest\x12\x11\n\tsubscribe\x18\x01 \x02(\x08\x12\x1a\n\x12publication_handle\x18\x02 \x02(\x04\"\x17\n\x15SetSubscribedResponse\"G\n\x16GetSessionStatsRequest\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\"+\n\x17GetSessionStatsResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\xf7\x01\n\x17GetSessionStatsCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x05\x65rror\x18\x02 \x01(\tH\x00\x12?\n\x06result\x18\x03 \x01(\x0b\x32-.livekit.proto.GetSessionStatsCallback.ResultH\x00\x1am\n\x06Result\x12\x30\n\x0fpublisher_stats\x18\x01 \x03(\x0b\x32\x17.livekit.proto.RtcStats\x12\x31\n\x10subscriber_stats\x18\x02 \x03(\x0b\x32\x17.livekit.proto.RtcStatsB\t\n\x07message\";\n\rVideoEncoding\x12\x13\n\x0bmax_bitrate\x18\x01 \x02(\x04\x12\x15\n\rmax_framerate\x18\x02 \x02(\x01\"$\n\rAudioEncoding\x12\x13\n\x0bmax_bitrate\x18\x01 \x02(\x04\"\x96\x04\n\x13TrackPublishOptions\x12\x34\n\x0evideo_encoding\x18\x01 \x01(\x0b\x32\x1c.livekit.proto.VideoEncoding\x12\x34\n\x0e\x61udio_encoding\x18\x02 \x01(\x0b\x32\x1c.livekit.proto.AudioEncoding\x12.\n\x0bvideo_codec\x18\x03 \x01(\x0e\x32\x19.livekit.proto.VideoCodec\x12\x0b\n\x03\x64tx\x18\x04 \x01(\x08\x12\x0b\n\x03red\x18\x05 \x01(\x08\x12\x11\n\tsimulcast\x18\x06 \x01(\x08\x12*\n\x06source\x18\x07 \x01(\x0e\x32\x1a.livekit.proto.TrackSource\x12\x0e\n\x06stream\x18\x08 \x01(\t\x12\x19\n\x11preconnect_buffer\x18\t \x01(\x08\x12\x44\n\x17\x66rame_metadata_features\x18\n \x03(\x0e\x32#.livekit.proto.FrameMetadataFeature\x12\x18\n\x10scalability_mode\x18\x0b \x01(\t\x12\x39\n\rvideo_encoder\x18\x0c \x01(\x0e\x32\".livekit.proto.VideoEncoderBackend\x12\x44\n\x16\x64\x65gradation_preference\x18\r \x01(\x0e\x32$.livekit.proto.DegradationPreference\"=\n\tIceServer\x12\x0c\n\x04urls\x18\x01 \x03(\t\x12\x10\n\x08username\x18\x02 \x01(\t\x12\x10\n\x08password\x18\x03 \x01(\t\"\xc4\x01\n\tRtcConfig\x12;\n\x12ice_transport_type\x18\x01 \x01(\x0e\x32\x1f.livekit.proto.IceTransportType\x12K\n\x1a\x63ontinual_gathering_policy\x18\x02 \x01(\x0e\x32\'.livekit.proto.ContinualGatheringPolicy\x12-\n\x0bice_servers\x18\x03 \x03(\x0b\x32\x18.livekit.proto.IceServer\"b\n\x15RoomDataStreamOptions\x12\x1f\n\x17max_payload_byte_length\x18\x01 \x01(\x04\x12(\n use_legacy_client_implementation\x18\x02 \x01(\x08\"\xe9\x02\n\x0bRoomOptions\x12\x16\n\x0e\x61uto_subscribe\x18\x01 \x01(\x08\x12\x17\n\x0f\x61\x64\x61ptive_stream\x18\x02 \x01(\x08\x12\x10\n\x08\x64ynacast\x18\x03 \x01(\x08\x12,\n\x04\x65\x32\x65\x65\x18\x04 \x01(\x0b\x32\x1a.livekit.proto.E2eeOptionsB\x02\x18\x01\x12,\n\nrtc_config\x18\x05 \x01(\x0b\x32\x18.livekit.proto.RtcConfig\x12\x14\n\x0cjoin_retries\x18\x06 \x01(\r\x12.\n\nencryption\x18\x07 \x01(\x0b\x32\x1a.livekit.proto.E2eeOptions\x12\x1e\n\x16single_peer_connection\x18\x08 \x01(\x08\x12\x1a\n\x12\x63onnect_timeout_ms\x18\t \x01(\x04\x12\x39\n\x0b\x64\x61ta_stream\x18\n \x01(\x0b\x32$.livekit.proto.RoomDataStreamOptions\"w\n\x14TranscriptionSegment\x12\n\n\x02id\x18\x01 \x02(\t\x12\x0c\n\x04text\x18\x02 \x02(\t\x12\x12\n\nstart_time\x18\x03 \x02(\x04\x12\x10\n\x08\x65nd_time\x18\x04 \x02(\x04\x12\r\n\x05\x66inal\x18\x05 \x02(\x08\x12\x10\n\x08language\x18\x06 \x02(\t\"0\n\nBufferInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x02(\x04\x12\x10\n\x08\x64\x61ta_len\x18\x02 \x02(\x04\"e\n\x0bOwnedBuffer\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\'\n\x04\x64\x61ta\x18\x02 \x02(\x0b\x32\x19.livekit.proto.BufferInfo\"\xce\x17\n\tRoomEvent\x12\x13\n\x0broom_handle\x18\x01 \x02(\x04\x12\x44\n\x15participant_connected\x18\x02 \x01(\x0b\x32#.livekit.proto.ParticipantConnectedH\x00\x12J\n\x18participant_disconnected\x18\x03 \x01(\x0b\x32&.livekit.proto.ParticipantDisconnectedH\x00\x12\x43\n\x15local_track_published\x18\x04 \x01(\x0b\x32\".livekit.proto.LocalTrackPublishedH\x00\x12G\n\x17local_track_unpublished\x18\x05 \x01(\x0b\x32$.livekit.proto.LocalTrackUnpublishedH\x00\x12\x45\n\x16local_track_subscribed\x18\x06 \x01(\x0b\x32#.livekit.proto.LocalTrackSubscribedH\x00\x12\x38\n\x0ftrack_published\x18\x07 \x01(\x0b\x32\x1d.livekit.proto.TrackPublishedH\x00\x12<\n\x11track_unpublished\x18\x08 \x01(\x0b\x32\x1f.livekit.proto.TrackUnpublishedH\x00\x12:\n\x10track_subscribed\x18\t \x01(\x0b\x32\x1e.livekit.proto.TrackSubscribedH\x00\x12>\n\x12track_unsubscribed\x18\n \x01(\x0b\x32 .livekit.proto.TrackUnsubscribedH\x00\x12K\n\x19track_subscription_failed\x18\x0b \x01(\x0b\x32&.livekit.proto.TrackSubscriptionFailedH\x00\x12\x30\n\x0btrack_muted\x18\x0c \x01(\x0b\x32\x19.livekit.proto.TrackMutedH\x00\x12\x34\n\rtrack_unmuted\x18\r \x01(\x0b\x32\x1b.livekit.proto.TrackUnmutedH\x00\x12G\n\x17\x61\x63tive_speakers_changed\x18\x0e \x01(\x0b\x32$.livekit.proto.ActiveSpeakersChangedH\x00\x12\x43\n\x15room_metadata_changed\x18\x0f \x01(\x0b\x32\".livekit.proto.RoomMetadataChangedH\x00\x12\x39\n\x10room_sid_changed\x18\x10 \x01(\x0b\x32\x1d.livekit.proto.RoomSidChangedH\x00\x12Q\n\x1cparticipant_metadata_changed\x18\x11 \x01(\x0b\x32).livekit.proto.ParticipantMetadataChangedH\x00\x12I\n\x18participant_name_changed\x18\x12 \x01(\x0b\x32%.livekit.proto.ParticipantNameChangedH\x00\x12U\n\x1eparticipant_attributes_changed\x18\x13 \x01(\x0b\x32+.livekit.proto.ParticipantAttributesChangedH\x00\x12M\n\x1a\x63onnection_quality_changed\x18\x14 \x01(\x0b\x32\'.livekit.proto.ConnectionQualityChangedH\x00\x12I\n\x18\x63onnection_state_changed\x18\x15 \x01(\x0b\x32%.livekit.proto.ConnectionStateChangedH\x00\x12\x33\n\x0c\x64isconnected\x18\x16 \x01(\x0b\x32\x1b.livekit.proto.DisconnectedH\x00\x12\x33\n\x0creconnecting\x18\x17 \x01(\x0b\x32\x1b.livekit.proto.ReconnectingH\x00\x12\x31\n\x0breconnected\x18\x18 \x01(\x0b\x32\x1a.livekit.proto.ReconnectedH\x00\x12=\n\x12\x65\x32\x65\x65_state_changed\x18\x19 \x01(\x0b\x32\x1f.livekit.proto.E2eeStateChangedH\x00\x12%\n\x03\x65os\x18\x1a \x01(\x0b\x32\x16.livekit.proto.RoomEOSH\x00\x12\x41\n\x14\x64\x61ta_packet_received\x18\x1b \x01(\x0b\x32!.livekit.proto.DataPacketReceivedH\x00\x12\x46\n\x16transcription_received\x18\x1c \x01(\x0b\x32$.livekit.proto.TranscriptionReceivedH\x00\x12:\n\x0c\x63hat_message\x18\x1d \x01(\x0b\x32\".livekit.proto.ChatMessageReceivedH\x00\x12I\n\x16stream_header_received\x18\x1e \x01(\x0b\x32\'.livekit.proto.DataStreamHeaderReceivedH\x00\x12G\n\x15stream_chunk_received\x18\x1f \x01(\x0b\x32&.livekit.proto.DataStreamChunkReceivedH\x00\x12K\n\x17stream_trailer_received\x18 \x01(\x0b\x32(.livekit.proto.DataStreamTrailerReceivedH\x00\x12i\n\"data_channel_low_threshold_changed\x18! \x01(\x0b\x32;.livekit.proto.DataChannelBufferedAmountLowThresholdChangedH\x00\x12=\n\x12\x62yte_stream_opened\x18\" \x01(\x0b\x32\x1f.livekit.proto.ByteStreamOpenedH\x00\x12=\n\x12text_stream_opened\x18# \x01(\x0b\x32\x1f.livekit.proto.TextStreamOpenedH\x00\x12/\n\x0croom_updated\x18$ \x01(\x0b\x32\x17.livekit.proto.RoomInfoH\x00\x12(\n\x05moved\x18% \x01(\x0b\x32\x17.livekit.proto.RoomInfoH\x00\x12\x42\n\x14participants_updated\x18& \x01(\x0b\x32\".livekit.proto.ParticipantsUpdatedH\x00\x12\x62\n%participant_encryption_status_changed\x18\' \x01(\x0b\x32\x31.livekit.proto.ParticipantEncryptionStatusChangedH\x00\x12U\n\x1eparticipant_permission_changed\x18) \x01(\x0b\x32+.livekit.proto.ParticipantPermissionChangedH\x00\x12\x38\n\x0ftoken_refreshed\x18( \x01(\x0b\x32\x1d.livekit.proto.TokenRefreshedH\x00\x12>\n\x12participant_active\x18* \x01(\x0b\x32 .livekit.proto.ParticipantActiveH\x00\x12\x41\n\x14\x64\x61ta_track_published\x18+ \x01(\x0b\x32!.livekit.proto.DataTrackPublishedH\x00\x12\x45\n\x16\x64\x61ta_track_unpublished\x18, \x01(\x0b\x32#.livekit.proto.DataTrackUnpublishedH\x00\x12G\n\x17local_track_republished\x18- \x01(\x0b\x32$.livekit.proto.LocalTrackRepublishedH\x00\x42\t\n\x07message\"\xc9\x02\n\x08RoomInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x02(\t\x12\x10\n\x08metadata\x18\x03 \x02(\t\x12.\n&lossy_dc_buffered_amount_low_threshold\x18\x04 \x02(\x04\x12\x31\n)reliable_dc_buffered_amount_low_threshold\x18\x05 \x02(\x04\x12\x15\n\rempty_timeout\x18\x06 \x02(\r\x12\x19\n\x11\x64\x65parture_timeout\x18\x07 \x02(\r\x12\x18\n\x10max_participants\x18\x08 \x02(\r\x12\x15\n\rcreation_time\x18\t \x02(\x03\x12\x18\n\x10num_participants\x18\n \x02(\r\x12\x16\n\x0enum_publishers\x18\x0b \x02(\r\x12\x18\n\x10\x61\x63tive_recording\x18\x0c \x02(\x08\"a\n\tOwnedRoom\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12%\n\x04info\x18\x02 \x02(\x0b\x32\x17.livekit.proto.RoomInfo\"K\n\x13ParticipantsUpdated\x12\x34\n\x0cparticipants\x18\x01 \x03(\x0b\x32\x1e.livekit.proto.ParticipantInfo\"E\n\x14ParticipantConnected\x12-\n\x04info\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedParticipant\"1\n\x11ParticipantActive\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\"s\n\x17ParticipantDisconnected\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12:\n\x11\x64isconnect_reason\x18\x02 \x02(\x0e\x32\x1f.livekit.proto.DisconnectReason\"(\n\x13LocalTrackPublished\x12\x11\n\ttrack_sid\x18\x01 \x02(\t\"0\n\x15LocalTrackUnpublished\x12\x17\n\x0fpublication_sid\x18\x01 \x02(\t\"|\n\x15LocalTrackRepublished\x12\x1a\n\x12publication_handle\x18\x01 \x02(\x04\x12\x14\n\x0cprevious_sid\x18\x02 \x02(\t\x12\x31\n\x04info\x18\x03 \x02(\x0b\x32#.livekit.proto.TrackPublicationInfo\")\n\x14LocalTrackSubscribed\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\"i\n\x0eTrackPublished\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x39\n\x0bpublication\x18\x02 \x02(\x0b\x32$.livekit.proto.OwnedTrackPublication\"I\n\x10TrackUnpublished\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x17\n\x0fpublication_sid\x18\x02 \x02(\t\"Y\n\x0fTrackSubscribed\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12(\n\x05track\x18\x02 \x02(\x0b\x32\x19.livekit.proto.OwnedTrack\"D\n\x11TrackUnsubscribed\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\"Y\n\x17TrackSubscriptionFailed\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\x12\r\n\x05\x65rror\x18\x03 \x02(\t\"=\n\nTrackMuted\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\"?\n\x0cTrackUnmuted\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\ttrack_sid\x18\x02 \x02(\t\"_\n\x10\x45\x32\x65\x65StateChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12-\n\x05state\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.EncryptionState\"7\n\x15\x41\x63tiveSpeakersChanged\x12\x1e\n\x16participant_identities\x18\x01 \x03(\t\"\'\n\x13RoomMetadataChanged\x12\x10\n\x08metadata\x18\x01 \x02(\t\"\x1d\n\x0eRoomSidChanged\x12\x0b\n\x03sid\x18\x01 \x02(\t\"L\n\x1aParticipantMetadataChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x10\n\x08metadata\x18\x02 \x02(\t\"\xac\x01\n\x1cParticipantAttributesChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x32\n\nattributes\x18\x02 \x03(\x0b\x32\x1e.livekit.proto.AttributesEntry\x12:\n\x12\x63hanged_attributes\x18\x03 \x03(\x0b\x32\x1e.livekit.proto.AttributesEntry\"X\n\"ParticipantEncryptionStatusChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x14\n\x0cis_encrypted\x18\x02 \x02(\x08\"D\n\x16ParticipantNameChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x0c\n\x04name\x18\x02 \x02(\t\"v\n\x1cParticipantPermissionChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x38\n\npermission\x18\x02 \x01(\x0b\x32$.livekit.proto.ParticipantPermission\"k\n\x18\x43onnectionQualityChanged\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x31\n\x07quality\x18\x02 \x02(\x0e\x32 .livekit.proto.ConnectionQuality\"E\n\nUserPacket\x12(\n\x04\x64\x61ta\x18\x01 \x02(\x0b\x32\x1a.livekit.proto.OwnedBuffer\x12\r\n\x05topic\x18\x02 \x01(\t\"y\n\x0b\x43hatMessage\x12\n\n\x02id\x18\x01 \x02(\t\x12\x11\n\ttimestamp\x18\x02 \x02(\x03\x12\x0f\n\x07message\x18\x03 \x02(\t\x12\x16\n\x0e\x65\x64it_timestamp\x18\x04 \x01(\x03\x12\x0f\n\x07\x64\x65leted\x18\x05 \x01(\x08\x12\x11\n\tgenerated\x18\x06 \x01(\x08\"`\n\x13\x43hatMessageReceived\x12+\n\x07message\x18\x01 \x02(\x0b\x32\x1a.livekit.proto.ChatMessage\x12\x1c\n\x14participant_identity\x18\x02 \x02(\t\"&\n\x07SipDTMF\x12\x0c\n\x04\x63ode\x18\x01 \x02(\r\x12\r\n\x05\x64igit\x18\x02 \x01(\t\"\xbf\x01\n\x12\x44\x61taPacketReceived\x12+\n\x04kind\x18\x01 \x02(\x0e\x32\x1d.livekit.proto.DataPacketKind\x12\x1c\n\x14participant_identity\x18\x02 \x02(\t\x12)\n\x04user\x18\x04 \x01(\x0b\x32\x19.livekit.proto.UserPacketH\x00\x12*\n\x08sip_dtmf\x18\x05 \x01(\x0b\x32\x16.livekit.proto.SipDTMFH\x00\x42\x07\n\x05value\"\x7f\n\x15TranscriptionReceived\x12\x1c\n\x14participant_identity\x18\x01 \x01(\t\x12\x11\n\ttrack_sid\x18\x02 \x01(\t\x12\x35\n\x08segments\x18\x03 \x03(\x0b\x32#.livekit.proto.TranscriptionSegment\"G\n\x16\x43onnectionStateChanged\x12-\n\x05state\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.ConnectionState\"\x0b\n\tConnected\"?\n\x0c\x44isconnected\x12/\n\x06reason\x18\x01 \x02(\x0e\x32\x1f.livekit.proto.DisconnectReason\"\x0e\n\x0cReconnecting\"\r\n\x0bReconnected\"\x1f\n\x0eTokenRefreshed\x12\r\n\x05token\x18\x01 \x02(\t\"\t\n\x07RoomEOS\"\x8e\x07\n\nDataStream\x1a\xaa\x01\n\nTextHeader\x12?\n\x0eoperation_type\x18\x01 \x02(\x0e\x32\'.livekit.proto.DataStream.OperationType\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x1a\n\x12reply_to_stream_id\x18\x03 \x01(\t\x12\x1b\n\x13\x61ttached_stream_ids\x18\x04 \x03(\t\x12\x11\n\tgenerated\x18\x05 \x01(\x08\x1a\x1a\n\nByteHeader\x12\x0c\n\x04name\x18\x01 \x02(\t\x1a\xeb\x02\n\x06Header\x12\x11\n\tstream_id\x18\x01 \x02(\t\x12\x11\n\ttimestamp\x18\x02 \x02(\x03\x12\x11\n\tmime_type\x18\x03 \x02(\t\x12\r\n\x05topic\x18\x04 \x02(\t\x12\x14\n\x0ctotal_length\x18\x05 \x01(\x04\x12\x44\n\nattributes\x18\x06 \x03(\x0b\x32\x30.livekit.proto.DataStream.Header.AttributesEntry\x12;\n\x0btext_header\x18\x07 \x01(\x0b\x32$.livekit.proto.DataStream.TextHeaderH\x00\x12;\n\x0b\x62yte_header\x18\x08 \x01(\x0b\x32$.livekit.proto.DataStream.ByteHeaderH\x00\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x10\n\x0e\x63ontent_header\x1a]\n\x05\x43hunk\x12\x11\n\tstream_id\x18\x01 \x02(\t\x12\x13\n\x0b\x63hunk_index\x18\x02 \x02(\x04\x12\x0f\n\x07\x63ontent\x18\x03 \x02(\x0c\x12\x0f\n\x07version\x18\x04 \x01(\x05\x12\n\n\x02iv\x18\x05 \x01(\x0c\x1a\xa6\x01\n\x07Trailer\x12\x11\n\tstream_id\x18\x01 \x02(\t\x12\x0e\n\x06reason\x18\x02 \x02(\t\x12\x45\n\nattributes\x18\x03 \x03(\x0b\x32\x31.livekit.proto.DataStream.Trailer.AttributesEntry\x1a\x31\n\x0f\x41ttributesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"A\n\rOperationType\x12\n\n\x06\x43REATE\x10\x00\x12\n\n\x06UPDATE\x10\x01\x12\n\n\x06\x44\x45LETE\x10\x02\x12\x0c\n\x08REACTION\x10\x03\"j\n\x18\x44\x61taStreamHeaderReceived\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x30\n\x06header\x18\x02 \x02(\x0b\x32 .livekit.proto.DataStream.Header\"g\n\x17\x44\x61taStreamChunkReceived\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12.\n\x05\x63hunk\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.DataStream.Chunk\"m\n\x19\x44\x61taStreamTrailerReceived\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x32\n\x07trailer\x18\x02 \x02(\x0b\x32!.livekit.proto.DataStream.Trailer\"\xc0\x01\n\x17SendStreamHeaderRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x30\n\x06header\x18\x02 \x02(\x0b\x32 .livekit.proto.DataStream.Header\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\x17\n\x0fsender_identity\x18\x04 \x02(\t\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\"\xbd\x01\n\x16SendStreamChunkRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12.\n\x05\x63hunk\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.DataStream.Chunk\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\x17\n\x0fsender_identity\x18\x04 \x02(\t\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\"\xc3\x01\n\x18SendStreamTrailerRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x32\n\x07trailer\x18\x02 \x02(\x0b\x32!.livekit.proto.DataStream.Trailer\x12\x1e\n\x16\x64\x65stination_identities\x18\x03 \x03(\t\x12\x17\n\x0fsender_identity\x18\x04 \x02(\t\x12\x18\n\x10request_async_id\x18\x05 \x01(\x04\",\n\x18SendStreamHeaderResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"+\n\x17SendStreamChunkResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"-\n\x19SendStreamTrailerResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\";\n\x18SendStreamHeaderCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\":\n\x17SendStreamChunkCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"<\n\x19SendStreamTrailerCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\"\x93\x01\n/SetDataChannelBufferedAmountLowThresholdRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x11\n\tthreshold\x18\x02 \x02(\x04\x12+\n\x04kind\x18\x03 \x02(\x0e\x32\x1d.livekit.proto.DataPacketKind\"2\n0SetDataChannelBufferedAmountLowThresholdResponse\"n\n,DataChannelBufferedAmountLowThresholdChanged\x12+\n\x04kind\x18\x01 \x02(\x0e\x32\x1d.livekit.proto.DataPacketKind\x12\x11\n\tthreshold\x18\x02 \x02(\x04\"f\n\x10\x42yteStreamOpened\x12\x34\n\x06reader\x18\x01 \x02(\x0b\x32$.livekit.proto.OwnedByteStreamReader\x12\x1c\n\x14participant_identity\x18\x02 \x02(\t\"f\n\x10TextStreamOpened\x12\x34\n\x06reader\x18\x01 \x02(\x0b\x32$.livekit.proto.OwnedTextStreamReader\x12\x1c\n\x14participant_identity\x18\x02 \x02(\t\"H\n\x12\x44\x61taTrackPublished\x12\x32\n\x05track\x18\x01 \x02(\x0b\x32#.livekit.proto.OwnedRemoteDataTrack\"#\n\x14\x44\x61taTrackUnpublished\x12\x0b\n\x03sid\x18\x01 \x02(\t*\x90\x02\n\x14SimulateScenarioKind\x12\x1d\n\x19SIMULATE_SIGNAL_RECONNECT\x10\x00\x12\x14\n\x10SIMULATE_SPEAKER\x10\x01\x12\x19\n\x15SIMULATE_NODE_FAILURE\x10\x02\x12\x19\n\x15SIMULATE_SERVER_LEAVE\x10\x03\x12\x16\n\x12SIMULATE_MIGRATION\x10\x04\x12\x16\n\x12SIMULATE_FORCE_TCP\x10\x05\x12\x16\n\x12SIMULATE_FORCE_TLS\x10\x06\x12\x1b\n\x17SIMULATE_FULL_RECONNECT\x10\x07\x12(\n$SIMULATE_DISCONNECT_SIGNAL_ON_RESUME\x10\x08*\xc3\x01\n\x13VideoEncoderBackend\x12\x18\n\x14\x45NCODER_BACKEND_AUTO\x10\x00\x12\x1c\n\x18\x45NCODER_BACKEND_SOFTWARE\x10\x01\x12\x1c\n\x18\x45NCODER_BACKEND_HARDWARE\x10\x02\x12\x19\n\x15\x45NCODER_BACKEND_NVENC\x10\x03\x12\x19\n\x15\x45NCODER_BACKEND_VAAPI\x10\x04\x12 \n\x1c\x45NCODER_BACKEND_VIDEOTOOLBOX\x10\x05*\xd9\x01\n\x15\x44\x65gradationPreference\x12#\n\x1f\x44\x45GRADATION_PREFERENCE_BALANCED\x10\x00\x12-\n)DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE\x10\x01\x12.\n*DEGRADATION_PREFERENCE_MAINTAIN_RESOLUTION\x10\x02\x12<\n8DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE_AND_RESOLUTION\x10\x04*P\n\x10IceTransportType\x12\x13\n\x0fTRANSPORT_RELAY\x10\x00\x12\x14\n\x10TRANSPORT_NOHOST\x10\x01\x12\x11\n\rTRANSPORT_ALL\x10\x02*C\n\x18\x43ontinualGatheringPolicy\x12\x0f\n\x0bGATHER_ONCE\x10\x00\x12\x16\n\x12GATHER_CONTINUALLY\x10\x01*`\n\x11\x43onnectionQuality\x12\x10\n\x0cQUALITY_POOR\x10\x00\x12\x10\n\x0cQUALITY_GOOD\x10\x01\x12\x15\n\x11QUALITY_EXCELLENT\x10\x02\x12\x10\n\x0cQUALITY_LOST\x10\x03*S\n\x0f\x43onnectionState\x12\x15\n\x11\x43ONN_DISCONNECTED\x10\x00\x12\x12\n\x0e\x43ONN_CONNECTED\x10\x01\x12\x15\n\x11\x43ONN_RECONNECTING\x10\x02*3\n\x0e\x44\x61taPacketKind\x12\x0e\n\nKIND_LOSSY\x10\x00\x12\x11\n\rKIND_RELIABLE\x10\x01\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -28,152 +30,282 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_DATAPACKETRECEIVED'].fields_by_name['participant_sid']._options = None - _globals['_DATAPACKETRECEIVED'].fields_by_name['participant_sid']._serialized_options = b'\030\001' - _globals['_ICETRANSPORTTYPE']._serialized_start=7188 - _globals['_ICETRANSPORTTYPE']._serialized_end=7268 - _globals['_CONTINUALGATHERINGPOLICY']._serialized_start=7270 - _globals['_CONTINUALGATHERINGPOLICY']._serialized_end=7337 - _globals['_CONNECTIONQUALITY']._serialized_start=7339 - _globals['_CONNECTIONQUALITY']._serialized_end=7435 - _globals['_CONNECTIONSTATE']._serialized_start=7437 - _globals['_CONNECTIONSTATE']._serialized_end=7520 - _globals['_DATAPACKETKIND']._serialized_start=7522 - _globals['_DATAPACKETKIND']._serialized_end=7573 - _globals['_CONNECTREQUEST']._serialized_start=119 - _globals['_CONNECTREQUEST']._serialized_end=208 - _globals['_CONNECTRESPONSE']._serialized_start=210 - _globals['_CONNECTRESPONSE']._serialized_end=245 - _globals['_CONNECTCALLBACK']._serialized_start=248 - _globals['_CONNECTCALLBACK']._serialized_end=629 - _globals['_CONNECTCALLBACK_PARTICIPANTWITHTRACKS']._serialized_start=482 - _globals['_CONNECTCALLBACK_PARTICIPANTWITHTRACKS']._serialized_end=619 - _globals['_DISCONNECTREQUEST']._serialized_start=631 - _globals['_DISCONNECTREQUEST']._serialized_end=671 - _globals['_DISCONNECTRESPONSE']._serialized_start=673 - _globals['_DISCONNECTRESPONSE']._serialized_end=711 - _globals['_DISCONNECTCALLBACK']._serialized_start=713 - _globals['_DISCONNECTCALLBACK']._serialized_end=751 - _globals['_PUBLISHTRACKREQUEST']._serialized_start=754 - _globals['_PUBLISHTRACKREQUEST']._serialized_end=884 - _globals['_PUBLISHTRACKRESPONSE']._serialized_start=886 - _globals['_PUBLISHTRACKRESPONSE']._serialized_end=926 - _globals['_PUBLISHTRACKCALLBACK']._serialized_start=929 - _globals['_PUBLISHTRACKCALLBACK']._serialized_end=1058 - _globals['_UNPUBLISHTRACKREQUEST']._serialized_start=1060 - _globals['_UNPUBLISHTRACKREQUEST']._serialized_end=1163 - _globals['_UNPUBLISHTRACKRESPONSE']._serialized_start=1165 - _globals['_UNPUBLISHTRACKRESPONSE']._serialized_end=1207 - _globals['_UNPUBLISHTRACKCALLBACK']._serialized_start=1209 - _globals['_UNPUBLISHTRACKCALLBACK']._serialized_end=1281 - _globals['_PUBLISHDATAREQUEST']._serialized_start=1284 - _globals['_PUBLISHDATAREQUEST']._serialized_end=1475 - _globals['_PUBLISHDATARESPONSE']._serialized_start=1477 - _globals['_PUBLISHDATARESPONSE']._serialized_end=1516 - _globals['_PUBLISHDATACALLBACK']._serialized_start=1518 - _globals['_PUBLISHDATACALLBACK']._serialized_end=1587 - _globals['_PUBLISHTRANSCRIPTIONREQUEST']._serialized_start=1590 - _globals['_PUBLISHTRANSCRIPTIONREQUEST']._serialized_end=1774 - _globals['_PUBLISHTRANSCRIPTIONRESPONSE']._serialized_start=1776 - _globals['_PUBLISHTRANSCRIPTIONRESPONSE']._serialized_end=1824 - _globals['_PUBLISHTRANSCRIPTIONCALLBACK']._serialized_start=1826 - _globals['_PUBLISHTRANSCRIPTIONCALLBACK']._serialized_end=1904 - _globals['_UPDATELOCALMETADATAREQUEST']._serialized_start=1906 - _globals['_UPDATELOCALMETADATAREQUEST']._serialized_end=1986 - _globals['_UPDATELOCALMETADATARESPONSE']._serialized_start=1988 - _globals['_UPDATELOCALMETADATARESPONSE']._serialized_end=2035 - _globals['_UPDATELOCALMETADATACALLBACK']._serialized_start=2037 - _globals['_UPDATELOCALMETADATACALLBACK']._serialized_end=2084 - _globals['_UPDATELOCALNAMEREQUEST']._serialized_start=2086 - _globals['_UPDATELOCALNAMEREQUEST']._serialized_end=2158 - _globals['_UPDATELOCALNAMERESPONSE']._serialized_start=2160 - _globals['_UPDATELOCALNAMERESPONSE']._serialized_end=2203 - _globals['_UPDATELOCALNAMECALLBACK']._serialized_start=2205 - _globals['_UPDATELOCALNAMECALLBACK']._serialized_end=2248 - _globals['_SETSUBSCRIBEDREQUEST']._serialized_start=2250 - _globals['_SETSUBSCRIBEDREQUEST']._serialized_end=2319 - _globals['_SETSUBSCRIBEDRESPONSE']._serialized_start=2321 - _globals['_SETSUBSCRIBEDRESPONSE']._serialized_end=2344 - _globals['_GETSESSIONSTATSREQUEST']._serialized_start=2346 - _globals['_GETSESSIONSTATSREQUEST']._serialized_end=2391 - _globals['_GETSESSIONSTATSRESPONSE']._serialized_start=2393 - _globals['_GETSESSIONSTATSRESPONSE']._serialized_end=2436 - _globals['_GETSESSIONSTATSCALLBACK']._serialized_start=2439 - _globals['_GETSESSIONSTATSCALLBACK']._serialized_end=2613 - _globals['_VIDEOENCODING']._serialized_start=2615 - _globals['_VIDEOENCODING']._serialized_end=2674 - _globals['_AUDIOENCODING']._serialized_start=2676 - _globals['_AUDIOENCODING']._serialized_end=2712 - _globals['_TRACKPUBLISHOPTIONS']._serialized_start=2715 - _globals['_TRACKPUBLISHOPTIONS']._serialized_end=2981 - _globals['_ICESERVER']._serialized_start=2983 - _globals['_ICESERVER']._serialized_end=3044 - _globals['_RTCCONFIG']._serialized_start=3047 - _globals['_RTCCONFIG']._serialized_end=3307 - _globals['_ROOMOPTIONS']._serialized_start=3310 - _globals['_ROOMOPTIONS']._serialized_end=3534 - _globals['_TRANSCRIPTIONSEGMENT']._serialized_start=3536 - _globals['_TRANSCRIPTIONSEGMENT']._serialized_end=3637 - _globals['_BUFFERINFO']._serialized_start=3639 - _globals['_BUFFERINFO']._serialized_end=3687 - _globals['_OWNEDBUFFER']._serialized_start=3689 - _globals['_OWNEDBUFFER']._serialized_end=3790 - _globals['_ROOMEVENT']._serialized_start=3793 - _globals['_ROOMEVENT']._serialized_end=5329 - _globals['_ROOMINFO']._serialized_start=5331 - _globals['_ROOMINFO']._serialized_end=5386 - _globals['_OWNEDROOM']._serialized_start=5388 - _globals['_OWNEDROOM']._serialized_end=5485 - _globals['_PARTICIPANTCONNECTED']._serialized_start=5487 - _globals['_PARTICIPANTCONNECTED']._serialized_end=5556 - _globals['_PARTICIPANTDISCONNECTED']._serialized_start=5558 - _globals['_PARTICIPANTDISCONNECTED']._serialized_end=5608 - _globals['_LOCALTRACKPUBLISHED']._serialized_start=5610 - _globals['_LOCALTRACKPUBLISHED']._serialized_end=5650 - _globals['_LOCALTRACKUNPUBLISHED']._serialized_start=5652 - _globals['_LOCALTRACKUNPUBLISHED']._serialized_end=5700 - _globals['_TRACKPUBLISHED']._serialized_start=5702 - _globals['_TRACKPUBLISHED']._serialized_end=5802 - _globals['_TRACKUNPUBLISHED']._serialized_start=5804 - _globals['_TRACKUNPUBLISHED']._serialized_end=5872 - _globals['_TRACKSUBSCRIBED']._serialized_start=5874 - _globals['_TRACKSUBSCRIBED']._serialized_end=5958 - _globals['_TRACKUNSUBSCRIBED']._serialized_start=5960 - _globals['_TRACKUNSUBSCRIBED']._serialized_end=6023 - _globals['_TRACKSUBSCRIPTIONFAILED']._serialized_start=6025 - _globals['_TRACKSUBSCRIPTIONFAILED']._serialized_end=6109 - _globals['_TRACKMUTED']._serialized_start=6111 - _globals['_TRACKMUTED']._serialized_end=6167 - _globals['_TRACKUNMUTED']._serialized_start=6169 - _globals['_TRACKUNMUTED']._serialized_end=6227 - _globals['_E2EESTATECHANGED']._serialized_start=6229 - _globals['_E2EESTATECHANGED']._serialized_end=6319 - _globals['_ACTIVESPEAKERSCHANGED']._serialized_start=6321 - _globals['_ACTIVESPEAKERSCHANGED']._serialized_end=6370 - _globals['_ROOMMETADATACHANGED']._serialized_start=6372 - _globals['_ROOMMETADATACHANGED']._serialized_end=6411 - _globals['_PARTICIPANTMETADATACHANGED']._serialized_start=6413 - _globals['_PARTICIPANTMETADATACHANGED']._serialized_end=6484 - _globals['_PARTICIPANTNAMECHANGED']._serialized_start=6486 - _globals['_PARTICIPANTNAMECHANGED']._serialized_end=6549 - _globals['_CONNECTIONQUALITYCHANGED']._serialized_start=6551 - _globals['_CONNECTIONQUALITYCHANGED']._serialized_end=6653 - _globals['_USERPACKET']._serialized_start=6655 - _globals['_USERPACKET']._serialized_end=6739 - _globals['_SIPDTMF']._serialized_start=6741 - _globals['_SIPDTMF']._serialized_end=6794 - _globals['_DATAPACKETRECEIVED']._serialized_start=6797 - _globals['_DATAPACKETRECEIVED']._serialized_end=7042 - _globals['_CONNECTIONSTATECHANGED']._serialized_start=7044 - _globals['_CONNECTIONSTATECHANGED']._serialized_end=7115 - _globals['_CONNECTED']._serialized_start=7117 - _globals['_CONNECTED']._serialized_end=7128 - _globals['_DISCONNECTED']._serialized_start=7130 - _globals['_DISCONNECTED']._serialized_end=7144 - _globals['_RECONNECTING']._serialized_start=7146 - _globals['_RECONNECTING']._serialized_end=7160 - _globals['_RECONNECTED']._serialized_start=7162 - _globals['_RECONNECTED']._serialized_end=7175 - _globals['_ROOMEOS']._serialized_start=7177 - _globals['_ROOMEOS']._serialized_end=7186 + _globals['_PUBLISHDATAREQUEST'].fields_by_name['destination_sids']._options = None + _globals['_PUBLISHDATAREQUEST'].fields_by_name['destination_sids']._serialized_options = b'\030\001' + _globals['_ROOMOPTIONS'].fields_by_name['e2ee']._options = None + _globals['_ROOMOPTIONS'].fields_by_name['e2ee']._serialized_options = b'\030\001' + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._options = None + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._options = None + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_options = b'8\001' + _globals['_SIMULATESCENARIOKIND']._serialized_start=15187 + _globals['_SIMULATESCENARIOKIND']._serialized_end=15459 + _globals['_VIDEOENCODERBACKEND']._serialized_start=15462 + _globals['_VIDEOENCODERBACKEND']._serialized_end=15657 + _globals['_DEGRADATIONPREFERENCE']._serialized_start=15660 + _globals['_DEGRADATIONPREFERENCE']._serialized_end=15877 + _globals['_ICETRANSPORTTYPE']._serialized_start=15879 + _globals['_ICETRANSPORTTYPE']._serialized_end=15959 + _globals['_CONTINUALGATHERINGPOLICY']._serialized_start=15961 + _globals['_CONTINUALGATHERINGPOLICY']._serialized_end=16028 + _globals['_CONNECTIONQUALITY']._serialized_start=16030 + _globals['_CONNECTIONQUALITY']._serialized_end=16126 + _globals['_CONNECTIONSTATE']._serialized_start=16128 + _globals['_CONNECTIONSTATE']._serialized_end=16211 + _globals['_DATAPACKETKIND']._serialized_start=16213 + _globals['_DATAPACKETKIND']._serialized_end=16264 + _globals['_CONNECTREQUEST']._serialized_start=156 + _globals['_CONNECTREQUEST']._serialized_end=271 + _globals['_CONNECTRESPONSE']._serialized_start=273 + _globals['_CONNECTRESPONSE']._serialized_end=308 + _globals['_CONNECTCALLBACK']._serialized_start=311 + _globals['_CONNECTCALLBACK']._serialized_end=758 + _globals['_CONNECTCALLBACK_PARTICIPANTWITHTRACKS']._serialized_start=423 + _globals['_CONNECTCALLBACK_PARTICIPANTWITHTRACKS']._serialized_end=560 + _globals['_CONNECTCALLBACK_RESULT']._serialized_start=563 + _globals['_CONNECTCALLBACK_RESULT']._serialized_end=747 + _globals['_DISCONNECTREQUEST']._serialized_start=760 + _globals['_DISCONNECTREQUEST']._serialized_end=875 + _globals['_DISCONNECTRESPONSE']._serialized_start=877 + _globals['_DISCONNECTRESPONSE']._serialized_end=915 + _globals['_DISCONNECTCALLBACK']._serialized_start=917 + _globals['_DISCONNECTCALLBACK']._serialized_end=955 + _globals['_SIMULATESCENARIOREQUEST']._serialized_start=957 + _globals['_SIMULATESCENARIOREQUEST']._serialized_end=1084 + _globals['_SIMULATESCENARIORESPONSE']._serialized_start=1086 + _globals['_SIMULATESCENARIORESPONSE']._serialized_end=1130 + _globals['_SIMULATESCENARIOCALLBACK']._serialized_start=1132 + _globals['_SIMULATESCENARIOCALLBACK']._serialized_end=1191 + _globals['_PUBLISHTRACKREQUEST']._serialized_start=1194 + _globals['_PUBLISHTRACKREQUEST']._serialized_end=1350 + _globals['_PUBLISHTRACKRESPONSE']._serialized_start=1352 + _globals['_PUBLISHTRACKRESPONSE']._serialized_end=1392 + _globals['_PUBLISHTRACKCALLBACK']._serialized_start=1395 + _globals['_PUBLISHTRACKCALLBACK']._serialized_end=1524 + _globals['_READYFORROOMEVENTREQUEST']._serialized_start=1526 + _globals['_READYFORROOMEVENTREQUEST']._serialized_end=1573 + _globals['_READYFORROOMEVENTRESPONSE']._serialized_start=1575 + _globals['_READYFORROOMEVENTRESPONSE']._serialized_end=1602 + _globals['_UNPUBLISHTRACKREQUEST']._serialized_start=1605 + _globals['_UNPUBLISHTRACKREQUEST']._serialized_end=1734 + _globals['_UNPUBLISHTRACKRESPONSE']._serialized_start=1736 + _globals['_UNPUBLISHTRACKRESPONSE']._serialized_end=1778 + _globals['_UNPUBLISHTRACKCALLBACK']._serialized_start=1780 + _globals['_UNPUBLISHTRACKCALLBACK']._serialized_end=1837 + _globals['_PUBLISHDATAREQUEST']._serialized_start=1840 + _globals['_PUBLISHDATAREQUEST']._serialized_end=2051 + _globals['_PUBLISHDATARESPONSE']._serialized_start=2053 + _globals['_PUBLISHDATARESPONSE']._serialized_end=2092 + _globals['_PUBLISHDATACALLBACK']._serialized_start=2094 + _globals['_PUBLISHDATACALLBACK']._serialized_end=2148 + _globals['_PUBLISHTRANSCRIPTIONREQUEST']._serialized_start=2151 + _globals['_PUBLISHTRANSCRIPTIONREQUEST']._serialized_end=2343 + _globals['_PUBLISHTRANSCRIPTIONRESPONSE']._serialized_start=2345 + _globals['_PUBLISHTRANSCRIPTIONRESPONSE']._serialized_end=2393 + _globals['_PUBLISHTRANSCRIPTIONCALLBACK']._serialized_start=2395 + _globals['_PUBLISHTRANSCRIPTIONCALLBACK']._serialized_end=2458 + _globals['_PUBLISHSIPDTMFREQUEST']._serialized_start=2461 + _globals['_PUBLISHSIPDTMFREQUEST']._serialized_end=2605 + _globals['_PUBLISHSIPDTMFRESPONSE']._serialized_start=2607 + _globals['_PUBLISHSIPDTMFRESPONSE']._serialized_end=2649 + _globals['_PUBLISHSIPDTMFCALLBACK']._serialized_start=2651 + _globals['_PUBLISHSIPDTMFCALLBACK']._serialized_end=2708 + _globals['_SETLOCALMETADATAREQUEST']._serialized_start=2710 + _globals['_SETLOCALMETADATAREQUEST']._serialized_end=2813 + _globals['_SETLOCALMETADATARESPONSE']._serialized_start=2815 + _globals['_SETLOCALMETADATARESPONSE']._serialized_end=2859 + _globals['_SETLOCALMETADATACALLBACK']._serialized_start=2861 + _globals['_SETLOCALMETADATACALLBACK']._serialized_end=2920 + _globals['_SENDCHATMESSAGEREQUEST']._serialized_start=2923 + _globals['_SENDCHATMESSAGEREQUEST']._serialized_end=3081 + _globals['_EDITCHATMESSAGEREQUEST']._serialized_start=3084 + _globals['_EDITCHATMESSAGEREQUEST']._serialized_end=3298 + _globals['_SENDCHATMESSAGERESPONSE']._serialized_start=3300 + _globals['_SENDCHATMESSAGERESPONSE']._serialized_end=3343 + _globals['_SENDCHATMESSAGECALLBACK']._serialized_start=3345 + _globals['_SENDCHATMESSAGECALLBACK']._serialized_end=3468 + _globals['_SETLOCALATTRIBUTESREQUEST']._serialized_start=3471 + _globals['_SETLOCALATTRIBUTESREQUEST']._serialized_end=3610 + _globals['_ATTRIBUTESENTRY']._serialized_start=3612 + _globals['_ATTRIBUTESENTRY']._serialized_end=3657 + _globals['_SETLOCALATTRIBUTESRESPONSE']._serialized_start=3659 + _globals['_SETLOCALATTRIBUTESRESPONSE']._serialized_end=3705 + _globals['_SETLOCALATTRIBUTESCALLBACK']._serialized_start=3707 + _globals['_SETLOCALATTRIBUTESCALLBACK']._serialized_end=3768 + _globals['_SETLOCALNAMEREQUEST']._serialized_start=3770 + _globals['_SETLOCALNAMEREQUEST']._serialized_end=3865 + _globals['_SETLOCALNAMERESPONSE']._serialized_start=3867 + _globals['_SETLOCALNAMERESPONSE']._serialized_end=3907 + _globals['_SETLOCALNAMECALLBACK']._serialized_start=3909 + _globals['_SETLOCALNAMECALLBACK']._serialized_end=3964 + _globals['_SETSUBSCRIBEDREQUEST']._serialized_start=3966 + _globals['_SETSUBSCRIBEDREQUEST']._serialized_end=4035 + _globals['_SETSUBSCRIBEDRESPONSE']._serialized_start=4037 + _globals['_SETSUBSCRIBEDRESPONSE']._serialized_end=4060 + _globals['_GETSESSIONSTATSREQUEST']._serialized_start=4062 + _globals['_GETSESSIONSTATSREQUEST']._serialized_end=4133 + _globals['_GETSESSIONSTATSRESPONSE']._serialized_start=4135 + _globals['_GETSESSIONSTATSRESPONSE']._serialized_end=4178 + _globals['_GETSESSIONSTATSCALLBACK']._serialized_start=4181 + _globals['_GETSESSIONSTATSCALLBACK']._serialized_end=4428 + _globals['_GETSESSIONSTATSCALLBACK_RESULT']._serialized_start=4308 + _globals['_GETSESSIONSTATSCALLBACK_RESULT']._serialized_end=4417 + _globals['_VIDEOENCODING']._serialized_start=4430 + _globals['_VIDEOENCODING']._serialized_end=4489 + _globals['_AUDIOENCODING']._serialized_start=4491 + _globals['_AUDIOENCODING']._serialized_end=4527 + _globals['_TRACKPUBLISHOPTIONS']._serialized_start=4530 + _globals['_TRACKPUBLISHOPTIONS']._serialized_end=5064 + _globals['_ICESERVER']._serialized_start=5066 + _globals['_ICESERVER']._serialized_end=5127 + _globals['_RTCCONFIG']._serialized_start=5130 + _globals['_RTCCONFIG']._serialized_end=5326 + _globals['_ROOMDATASTREAMOPTIONS']._serialized_start=5328 + _globals['_ROOMDATASTREAMOPTIONS']._serialized_end=5426 + _globals['_ROOMOPTIONS']._serialized_start=5429 + _globals['_ROOMOPTIONS']._serialized_end=5790 + _globals['_TRANSCRIPTIONSEGMENT']._serialized_start=5792 + _globals['_TRANSCRIPTIONSEGMENT']._serialized_end=5911 + _globals['_BUFFERINFO']._serialized_start=5913 + _globals['_BUFFERINFO']._serialized_end=5961 + _globals['_OWNEDBUFFER']._serialized_start=5963 + _globals['_OWNEDBUFFER']._serialized_end=6064 + _globals['_ROOMEVENT']._serialized_start=6067 + _globals['_ROOMEVENT']._serialized_end=9089 + _globals['_ROOMINFO']._serialized_start=9092 + _globals['_ROOMINFO']._serialized_end=9421 + _globals['_OWNEDROOM']._serialized_start=9423 + _globals['_OWNEDROOM']._serialized_end=9520 + _globals['_PARTICIPANTSUPDATED']._serialized_start=9522 + _globals['_PARTICIPANTSUPDATED']._serialized_end=9597 + _globals['_PARTICIPANTCONNECTED']._serialized_start=9599 + _globals['_PARTICIPANTCONNECTED']._serialized_end=9668 + _globals['_PARTICIPANTACTIVE']._serialized_start=9670 + _globals['_PARTICIPANTACTIVE']._serialized_end=9719 + _globals['_PARTICIPANTDISCONNECTED']._serialized_start=9721 + _globals['_PARTICIPANTDISCONNECTED']._serialized_end=9836 + _globals['_LOCALTRACKPUBLISHED']._serialized_start=9838 + _globals['_LOCALTRACKPUBLISHED']._serialized_end=9878 + _globals['_LOCALTRACKUNPUBLISHED']._serialized_start=9880 + _globals['_LOCALTRACKUNPUBLISHED']._serialized_end=9928 + _globals['_LOCALTRACKREPUBLISHED']._serialized_start=9930 + _globals['_LOCALTRACKREPUBLISHED']._serialized_end=10054 + _globals['_LOCALTRACKSUBSCRIBED']._serialized_start=10056 + _globals['_LOCALTRACKSUBSCRIBED']._serialized_end=10097 + _globals['_TRACKPUBLISHED']._serialized_start=10099 + _globals['_TRACKPUBLISHED']._serialized_end=10204 + _globals['_TRACKUNPUBLISHED']._serialized_start=10206 + _globals['_TRACKUNPUBLISHED']._serialized_end=10279 + _globals['_TRACKSUBSCRIBED']._serialized_start=10281 + _globals['_TRACKSUBSCRIBED']._serialized_end=10370 + _globals['_TRACKUNSUBSCRIBED']._serialized_start=10372 + _globals['_TRACKUNSUBSCRIBED']._serialized_end=10440 + _globals['_TRACKSUBSCRIPTIONFAILED']._serialized_start=10442 + _globals['_TRACKSUBSCRIPTIONFAILED']._serialized_end=10531 + _globals['_TRACKMUTED']._serialized_start=10533 + _globals['_TRACKMUTED']._serialized_end=10594 + _globals['_TRACKUNMUTED']._serialized_start=10596 + _globals['_TRACKUNMUTED']._serialized_end=10659 + _globals['_E2EESTATECHANGED']._serialized_start=10661 + _globals['_E2EESTATECHANGED']._serialized_end=10756 + _globals['_ACTIVESPEAKERSCHANGED']._serialized_start=10758 + _globals['_ACTIVESPEAKERSCHANGED']._serialized_end=10813 + _globals['_ROOMMETADATACHANGED']._serialized_start=10815 + _globals['_ROOMMETADATACHANGED']._serialized_end=10854 + _globals['_ROOMSIDCHANGED']._serialized_start=10856 + _globals['_ROOMSIDCHANGED']._serialized_end=10885 + _globals['_PARTICIPANTMETADATACHANGED']._serialized_start=10887 + _globals['_PARTICIPANTMETADATACHANGED']._serialized_end=10963 + _globals['_PARTICIPANTATTRIBUTESCHANGED']._serialized_start=10966 + _globals['_PARTICIPANTATTRIBUTESCHANGED']._serialized_end=11138 + _globals['_PARTICIPANTENCRYPTIONSTATUSCHANGED']._serialized_start=11140 + _globals['_PARTICIPANTENCRYPTIONSTATUSCHANGED']._serialized_end=11228 + _globals['_PARTICIPANTNAMECHANGED']._serialized_start=11230 + _globals['_PARTICIPANTNAMECHANGED']._serialized_end=11298 + _globals['_PARTICIPANTPERMISSIONCHANGED']._serialized_start=11300 + _globals['_PARTICIPANTPERMISSIONCHANGED']._serialized_end=11418 + _globals['_CONNECTIONQUALITYCHANGED']._serialized_start=11420 + _globals['_CONNECTIONQUALITYCHANGED']._serialized_end=11527 + _globals['_USERPACKET']._serialized_start=11529 + _globals['_USERPACKET']._serialized_end=11598 + _globals['_CHATMESSAGE']._serialized_start=11600 + _globals['_CHATMESSAGE']._serialized_end=11721 + _globals['_CHATMESSAGERECEIVED']._serialized_start=11723 + _globals['_CHATMESSAGERECEIVED']._serialized_end=11819 + _globals['_SIPDTMF']._serialized_start=11821 + _globals['_SIPDTMF']._serialized_end=11859 + _globals['_DATAPACKETRECEIVED']._serialized_start=11862 + _globals['_DATAPACKETRECEIVED']._serialized_end=12053 + _globals['_TRANSCRIPTIONRECEIVED']._serialized_start=12055 + _globals['_TRANSCRIPTIONRECEIVED']._serialized_end=12182 + _globals['_CONNECTIONSTATECHANGED']._serialized_start=12184 + _globals['_CONNECTIONSTATECHANGED']._serialized_end=12255 + _globals['_CONNECTED']._serialized_start=12257 + _globals['_CONNECTED']._serialized_end=12268 + _globals['_DISCONNECTED']._serialized_start=12270 + _globals['_DISCONNECTED']._serialized_end=12333 + _globals['_RECONNECTING']._serialized_start=12335 + _globals['_RECONNECTING']._serialized_end=12349 + _globals['_RECONNECTED']._serialized_start=12351 + _globals['_RECONNECTED']._serialized_end=12364 + _globals['_TOKENREFRESHED']._serialized_start=12366 + _globals['_TOKENREFRESHED']._serialized_end=12397 + _globals['_ROOMEOS']._serialized_start=12399 + _globals['_ROOMEOS']._serialized_end=12408 + _globals['_DATASTREAM']._serialized_start=12411 + _globals['_DATASTREAM']._serialized_end=13321 + _globals['_DATASTREAM_TEXTHEADER']._serialized_start=12426 + _globals['_DATASTREAM_TEXTHEADER']._serialized_end=12596 + _globals['_DATASTREAM_BYTEHEADER']._serialized_start=12598 + _globals['_DATASTREAM_BYTEHEADER']._serialized_end=12624 + _globals['_DATASTREAM_HEADER']._serialized_start=12627 + _globals['_DATASTREAM_HEADER']._serialized_end=12990 + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_start=12923 + _globals['_DATASTREAM_HEADER_ATTRIBUTESENTRY']._serialized_end=12972 + _globals['_DATASTREAM_CHUNK']._serialized_start=12992 + _globals['_DATASTREAM_CHUNK']._serialized_end=13085 + _globals['_DATASTREAM_TRAILER']._serialized_start=13088 + _globals['_DATASTREAM_TRAILER']._serialized_end=13254 + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_start=12923 + _globals['_DATASTREAM_TRAILER_ATTRIBUTESENTRY']._serialized_end=12972 + _globals['_DATASTREAM_OPERATIONTYPE']._serialized_start=13256 + _globals['_DATASTREAM_OPERATIONTYPE']._serialized_end=13321 + _globals['_DATASTREAMHEADERRECEIVED']._serialized_start=13323 + _globals['_DATASTREAMHEADERRECEIVED']._serialized_end=13429 + _globals['_DATASTREAMCHUNKRECEIVED']._serialized_start=13431 + _globals['_DATASTREAMCHUNKRECEIVED']._serialized_end=13534 + _globals['_DATASTREAMTRAILERRECEIVED']._serialized_start=13536 + _globals['_DATASTREAMTRAILERRECEIVED']._serialized_end=13645 + _globals['_SENDSTREAMHEADERREQUEST']._serialized_start=13648 + _globals['_SENDSTREAMHEADERREQUEST']._serialized_end=13840 + _globals['_SENDSTREAMCHUNKREQUEST']._serialized_start=13843 + _globals['_SENDSTREAMCHUNKREQUEST']._serialized_end=14032 + _globals['_SENDSTREAMTRAILERREQUEST']._serialized_start=14035 + _globals['_SENDSTREAMTRAILERREQUEST']._serialized_end=14230 + _globals['_SENDSTREAMHEADERRESPONSE']._serialized_start=14232 + _globals['_SENDSTREAMHEADERRESPONSE']._serialized_end=14276 + _globals['_SENDSTREAMCHUNKRESPONSE']._serialized_start=14278 + _globals['_SENDSTREAMCHUNKRESPONSE']._serialized_end=14321 + _globals['_SENDSTREAMTRAILERRESPONSE']._serialized_start=14323 + _globals['_SENDSTREAMTRAILERRESPONSE']._serialized_end=14368 + _globals['_SENDSTREAMHEADERCALLBACK']._serialized_start=14370 + _globals['_SENDSTREAMHEADERCALLBACK']._serialized_end=14429 + _globals['_SENDSTREAMCHUNKCALLBACK']._serialized_start=14431 + _globals['_SENDSTREAMCHUNKCALLBACK']._serialized_end=14489 + _globals['_SENDSTREAMTRAILERCALLBACK']._serialized_start=14491 + _globals['_SENDSTREAMTRAILERCALLBACK']._serialized_end=14551 + _globals['_SETDATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDREQUEST']._serialized_start=14554 + _globals['_SETDATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDREQUEST']._serialized_end=14701 + _globals['_SETDATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDRESPONSE']._serialized_start=14703 + _globals['_SETDATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDRESPONSE']._serialized_end=14753 + _globals['_DATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDCHANGED']._serialized_start=14755 + _globals['_DATACHANNELBUFFEREDAMOUNTLOWTHRESHOLDCHANGED']._serialized_end=14865 + _globals['_BYTESTREAMOPENED']._serialized_start=14867 + _globals['_BYTESTREAMOPENED']._serialized_end=14969 + _globals['_TEXTSTREAMOPENED']._serialized_start=14971 + _globals['_TEXTSTREAMOPENED']._serialized_end=15073 + _globals['_DATATRACKPUBLISHED']._serialized_start=15075 + _globals['_DATATRACKPUBLISHED']._serialized_end=15147 + _globals['_DATATRACKUNPUBLISHED']._serialized_start=15149 + _globals['_DATATRACKUNPUBLISHED']._serialized_end=15184 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/room_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/room_pb2.pyi index e2d2ca05..ac25a636 100644 --- a/livekit-rtc/livekit/rtc/_proto/room_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/room_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,8 +15,11 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import collections.abc +from . import data_stream_pb2 +from . import data_track_pb2 from . import e2ee_pb2 import google.protobuf.descriptor import google.protobuf.internal.containers @@ -37,6 +40,111 @@ else: DESCRIPTOR: google.protobuf.descriptor.FileDescriptor +class _SimulateScenarioKind: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _SimulateScenarioKindEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_SimulateScenarioKind.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + SIMULATE_SIGNAL_RECONNECT: _SimulateScenarioKind.ValueType # 0 + """Closes the signal channel locally; engine attempts a Resume.""" + SIMULATE_SPEAKER: _SimulateScenarioKind.ValueType # 1 + SIMULATE_NODE_FAILURE: _SimulateScenarioKind.ValueType # 2 + SIMULATE_SERVER_LEAVE: _SimulateScenarioKind.ValueType # 3 + SIMULATE_MIGRATION: _SimulateScenarioKind.ValueType # 4 + SIMULATE_FORCE_TCP: _SimulateScenarioKind.ValueType # 5 + SIMULATE_FORCE_TLS: _SimulateScenarioKind.ValueType # 6 + SIMULATE_FULL_RECONNECT: _SimulateScenarioKind.ValueType # 7 + """Asks the server to send `LeaveRequest{Reconnect}`, forcing a full + reconnect (new RtcSession; SDK republishes existing local tracks). + """ + SIMULATE_DISCONNECT_SIGNAL_ON_RESUME: _SimulateScenarioKind.ValueType # 8 + """Asks the server to drop the signalling connection during the next resume, + then triggers a resume locally. The resume cannot complete, so the engine + escalates to a full reconnect — exercising the resume→full escalation path. + """ + +class SimulateScenarioKind(_SimulateScenarioKind, metaclass=_SimulateScenarioKindEnumTypeWrapper): + """Simulate a reconnection scenario for testing. Mirrors the variants of + `livekit::SimulateScenario`. The Resume / FullReconnect variants are + the relevant ones for verifying that resume preserves publications and + full reconnect republishes them exactly once. + """ + +SIMULATE_SIGNAL_RECONNECT: SimulateScenarioKind.ValueType # 0 +"""Closes the signal channel locally; engine attempts a Resume.""" +SIMULATE_SPEAKER: SimulateScenarioKind.ValueType # 1 +SIMULATE_NODE_FAILURE: SimulateScenarioKind.ValueType # 2 +SIMULATE_SERVER_LEAVE: SimulateScenarioKind.ValueType # 3 +SIMULATE_MIGRATION: SimulateScenarioKind.ValueType # 4 +SIMULATE_FORCE_TCP: SimulateScenarioKind.ValueType # 5 +SIMULATE_FORCE_TLS: SimulateScenarioKind.ValueType # 6 +SIMULATE_FULL_RECONNECT: SimulateScenarioKind.ValueType # 7 +"""Asks the server to send `LeaveRequest{Reconnect}`, forcing a full +reconnect (new RtcSession; SDK republishes existing local tracks). +""" +SIMULATE_DISCONNECT_SIGNAL_ON_RESUME: SimulateScenarioKind.ValueType # 8 +"""Asks the server to drop the signalling connection during the next resume, +then triggers a resume locally. The resume cannot complete, so the engine +escalates to a full reconnect — exercising the resume→full escalation path. +""" +global___SimulateScenarioKind = SimulateScenarioKind + +class _VideoEncoderBackend: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _VideoEncoderBackendEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_VideoEncoderBackend.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + ENCODER_BACKEND_AUTO: _VideoEncoderBackend.ValueType # 0 + ENCODER_BACKEND_SOFTWARE: _VideoEncoderBackend.ValueType # 1 + ENCODER_BACKEND_HARDWARE: _VideoEncoderBackend.ValueType # 2 + ENCODER_BACKEND_NVENC: _VideoEncoderBackend.ValueType # 3 + ENCODER_BACKEND_VAAPI: _VideoEncoderBackend.ValueType # 4 + ENCODER_BACKEND_VIDEOTOOLBOX: _VideoEncoderBackend.ValueType # 5 + +class VideoEncoderBackend(_VideoEncoderBackend, metaclass=_VideoEncoderBackendEnumTypeWrapper): ... + +ENCODER_BACKEND_AUTO: VideoEncoderBackend.ValueType # 0 +ENCODER_BACKEND_SOFTWARE: VideoEncoderBackend.ValueType # 1 +ENCODER_BACKEND_HARDWARE: VideoEncoderBackend.ValueType # 2 +ENCODER_BACKEND_NVENC: VideoEncoderBackend.ValueType # 3 +ENCODER_BACKEND_VAAPI: VideoEncoderBackend.ValueType # 4 +ENCODER_BACKEND_VIDEOTOOLBOX: VideoEncoderBackend.ValueType # 5 +global___VideoEncoderBackend = VideoEncoderBackend + +class _DegradationPreference: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _DegradationPreferenceEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_DegradationPreference.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + DEGRADATION_PREFERENCE_BALANCED: _DegradationPreference.ValueType # 0 + """Balance between framerate and resolution degradation.""" + DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE: _DegradationPreference.ValueType # 1 + """Degrade framerate to maintain resolution.""" + DEGRADATION_PREFERENCE_MAINTAIN_RESOLUTION: _DegradationPreference.ValueType # 2 + """Degrade resolution to maintain framerate (drop frames to keep clarity).""" + DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE_AND_RESOLUTION: _DegradationPreference.ValueType # 4 + """Maintain both framerate and resolution. Frames may be dropped before encoding + if necessary to avoid overusing network and encoder resources. + """ + +class DegradationPreference(_DegradationPreference, metaclass=_DegradationPreferenceEnumTypeWrapper): + """Controls how the encoder degrades quality when bandwidth is constrained.""" + +DEGRADATION_PREFERENCE_BALANCED: DegradationPreference.ValueType # 0 +"""Balance between framerate and resolution degradation.""" +DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE: DegradationPreference.ValueType # 1 +"""Degrade framerate to maintain resolution.""" +DEGRADATION_PREFERENCE_MAINTAIN_RESOLUTION: DegradationPreference.ValueType # 2 +"""Degrade resolution to maintain framerate (drop frames to keep clarity).""" +DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE_AND_RESOLUTION: DegradationPreference.ValueType # 4 +"""Maintain both framerate and resolution. Frames may be dropped before encoding +if necessary to avoid overusing network and encoder resources. +""" +global___DegradationPreference = DegradationPreference + class _IceTransportType: ValueType = typing.NewType("ValueType", builtins.int) V: typing_extensions.TypeAlias = ValueType @@ -123,7 +231,7 @@ KIND_LOSSY: DataPacketKind.ValueType # 0 KIND_RELIABLE: DataPacketKind.ValueType # 1 global___DataPacketKind = DataPacketKind -@typing_extensions.final +@typing.final class ConnectRequest(google.protobuf.message.Message): """Connect to a new LiveKit room""" @@ -132,23 +240,26 @@ class ConnectRequest(google.protobuf.message.Message): URL_FIELD_NUMBER: builtins.int TOKEN_FIELD_NUMBER: builtins.int OPTIONS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int url: builtins.str token: builtins.str + request_async_id: builtins.int @property def options(self) -> global___RoomOptions: ... def __init__( self, *, - url: builtins.str = ..., - token: builtins.str = ..., + url: builtins.str | None = ..., + token: builtins.str | None = ..., options: global___RoomOptions | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["options", b"options"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["options", b"options", "token", b"token", "url", b"url"]) -> None: ... + def HasField(self, field_name: typing.Literal["options", b"options", "request_async_id", b"request_async_id", "token", b"token", "url", b"url"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["options", b"options", "request_async_id", b"request_async_id", "token", b"token", "url", b"url"]) -> None: ... global___ConnectRequest = ConnectRequest -@typing_extensions.final +@typing.final class ConnectResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -157,17 +268,18 @@ class ConnectResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___ConnectResponse = ConnectResponse -@typing_extensions.final +@typing.final class ConnectCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class ParticipantWithTracks(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -180,61 +292,84 @@ class ConnectCallback(google.protobuf.message.Message): """TrackInfo are not needed here, if we're subscribed to a track, the FfiServer will send a TrackSubscribed event """ + def __init__( self, *, participant: participant_pb2.OwnedParticipant | None = ..., publications: collections.abc.Iterable[track_pb2.OwnedTrackPublication] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["participant", b"participant"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["participant", b"participant", "publications", b"publications"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant", b"participant"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant", b"participant", "publications", b"publications"]) -> None: ... + + @typing.final + class Result(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROOM_FIELD_NUMBER: builtins.int + LOCAL_PARTICIPANT_FIELD_NUMBER: builtins.int + PARTICIPANTS_FIELD_NUMBER: builtins.int + @property + def room(self) -> global___OwnedRoom: ... + @property + def local_participant(self) -> participant_pb2.OwnedParticipant: ... + @property + def participants(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ConnectCallback.ParticipantWithTracks]: ... + def __init__( + self, + *, + room: global___OwnedRoom | None = ..., + local_participant: participant_pb2.OwnedParticipant | None = ..., + participants: collections.abc.Iterable[global___ConnectCallback.ParticipantWithTracks] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant", b"local_participant", "room", b"room"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant", b"local_participant", "participants", b"participants", "room", b"room"]) -> None: ... ASYNC_ID_FIELD_NUMBER: builtins.int ERROR_FIELD_NUMBER: builtins.int - ROOM_FIELD_NUMBER: builtins.int - LOCAL_PARTICIPANT_FIELD_NUMBER: builtins.int - PARTICIPANTS_FIELD_NUMBER: builtins.int + RESULT_FIELD_NUMBER: builtins.int async_id: builtins.int error: builtins.str @property - def room(self) -> global___OwnedRoom: ... - @property - def local_participant(self) -> participant_pb2.OwnedParticipant: ... - @property - def participants(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ConnectCallback.ParticipantWithTracks]: ... + def result(self) -> global___ConnectCallback.Result: ... def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., - room: global___OwnedRoom | None = ..., - local_participant: participant_pb2.OwnedParticipant | None = ..., - participants: collections.abc.Iterable[global___ConnectCallback.ParticipantWithTracks] | None = ..., + result: global___ConnectCallback.Result | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error", "local_participant", b"local_participant", "room", b"room"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error", "local_participant", b"local_participant", "participants", b"participants", "room", b"room"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["error", "result"] | None: ... global___ConnectCallback = ConnectCallback -@typing_extensions.final +@typing.final class DisconnectRequest(google.protobuf.message.Message): """Disconnect from the a room""" DESCRIPTOR: google.protobuf.descriptor.Descriptor ROOM_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int room_handle: builtins.int + request_async_id: builtins.int + reason: participant_pb2.DisconnectReason.ValueType def __init__( self, *, - room_handle: builtins.int = ..., + room_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., + reason: participant_pb2.DisconnectReason.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["room_handle", b"room_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["reason", b"reason", "request_async_id", b"request_async_id", "room_handle", b"room_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reason", b"reason", "request_async_id", b"request_async_id", "room_handle", b"room_handle"]) -> None: ... global___DisconnectRequest = DisconnectRequest -@typing_extensions.final +@typing.final class DisconnectResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -243,13 +378,14 @@ class DisconnectResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___DisconnectResponse = DisconnectResponse -@typing_extensions.final +@typing.final class DisconnectCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -258,13 +394,71 @@ class DisconnectCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___DisconnectCallback = DisconnectCallback -@typing_extensions.final +@typing.final +class SimulateScenarioRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROOM_HANDLE_FIELD_NUMBER: builtins.int + SCENARIO_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + room_handle: builtins.int + scenario: global___SimulateScenarioKind.ValueType + request_async_id: builtins.int + def __init__( + self, + *, + room_handle: builtins.int | None = ..., + scenario: global___SimulateScenarioKind.ValueType | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "room_handle", b"room_handle", "scenario", b"scenario"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "room_handle", b"room_handle", "scenario", b"scenario"]) -> None: ... + +global___SimulateScenarioRequest = SimulateScenarioRequest + +@typing.final +class SimulateScenarioResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___SimulateScenarioResponse = SimulateScenarioResponse + +@typing.final +class SimulateScenarioCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___SimulateScenarioCallback = SimulateScenarioCallback + +@typing.final class PublishTrackRequest(google.protobuf.message.Message): """Publish a track to the room""" @@ -273,23 +467,26 @@ class PublishTrackRequest(google.protobuf.message.Message): LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int TRACK_HANDLE_FIELD_NUMBER: builtins.int OPTIONS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int track_handle: builtins.int + request_async_id: builtins.int @property def options(self) -> global___TrackPublishOptions: ... def __init__( self, *, - local_participant_handle: builtins.int = ..., - track_handle: builtins.int = ..., + local_participant_handle: builtins.int | None = ..., + track_handle: builtins.int | None = ..., options: global___TrackPublishOptions | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["options", b"options"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "track_handle", b"track_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "options", b"options", "request_async_id", b"request_async_id", "track_handle", b"track_handle"]) -> None: ... global___PublishTrackRequest = PublishTrackRequest -@typing_extensions.final +@typing.final class PublishTrackResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -298,13 +495,14 @@ class PublishTrackResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___PublishTrackResponse = PublishTrackResponse -@typing_extensions.final +@typing.final class PublishTrackCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -318,17 +516,43 @@ class PublishTrackCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., publication: track_pb2.OwnedTrackPublication | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error", "publication", b"publication"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error", "publication", b"publication"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "publication", b"publication"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "publication", b"publication"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["error", "publication"] | None: ... global___PublishTrackCallback = PublishTrackCallback -@typing_extensions.final +@typing.final +class ReadyForRoomEventRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ROOM_HANDLE_FIELD_NUMBER: builtins.int + room_handle: builtins.int + def __init__( + self, + *, + room_handle: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["room_handle", b"room_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["room_handle", b"room_handle"]) -> None: ... + +global___ReadyForRoomEventRequest = ReadyForRoomEventRequest + +@typing.final +class ReadyForRoomEventResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___ReadyForRoomEventResponse = ReadyForRoomEventResponse + +@typing.final class UnpublishTrackRequest(google.protobuf.message.Message): """Unpublish a track from the room""" @@ -337,21 +561,25 @@ class UnpublishTrackRequest(google.protobuf.message.Message): LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int TRACK_SID_FIELD_NUMBER: builtins.int STOP_ON_UNPUBLISH_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int track_sid: builtins.str stop_on_unpublish: builtins.bool + request_async_id: builtins.int def __init__( self, *, - local_participant_handle: builtins.int = ..., - track_sid: builtins.str = ..., - stop_on_unpublish: builtins.bool = ..., + local_participant_handle: builtins.int | None = ..., + track_sid: builtins.str | None = ..., + stop_on_unpublish: builtins.bool | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["local_participant_handle", b"local_participant_handle", "stop_on_unpublish", b"stop_on_unpublish", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "stop_on_unpublish", b"stop_on_unpublish", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "stop_on_unpublish", b"stop_on_unpublish", "track_sid", b"track_sid"]) -> None: ... global___UnpublishTrackRequest = UnpublishTrackRequest -@typing_extensions.final +@typing.final class UnpublishTrackResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -360,13 +588,14 @@ class UnpublishTrackResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___UnpublishTrackResponse = UnpublishTrackResponse -@typing_extensions.final +@typing.final class UnpublishTrackCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -377,16 +606,15 @@ class UnpublishTrackCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... global___UnpublishTrackCallback = UnpublishTrackCallback -@typing_extensions.final +@typing.final class PublishDataRequest(google.protobuf.message.Message): """Publish data to other participants""" @@ -395,34 +623,39 @@ class PublishDataRequest(google.protobuf.message.Message): LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int DATA_PTR_FIELD_NUMBER: builtins.int DATA_LEN_FIELD_NUMBER: builtins.int - KIND_FIELD_NUMBER: builtins.int + RELIABLE_FIELD_NUMBER: builtins.int DESTINATION_SIDS_FIELD_NUMBER: builtins.int TOPIC_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int data_ptr: builtins.int data_len: builtins.int - kind: global___DataPacketKind.ValueType - @property - def destination_sids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: - """destination""" + reliable: builtins.bool topic: builtins.str + request_async_id: builtins.int + @property + def destination_sids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... def __init__( self, *, - local_participant_handle: builtins.int = ..., - data_ptr: builtins.int = ..., - data_len: builtins.int = ..., - kind: global___DataPacketKind.ValueType = ..., + local_participant_handle: builtins.int | None = ..., + data_ptr: builtins.int | None = ..., + data_len: builtins.int | None = ..., + reliable: builtins.bool | None = ..., destination_sids: collections.abc.Iterable[builtins.str] | None = ..., topic: builtins.str | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_topic", b"_topic", "topic", b"topic"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_topic", b"_topic", "data_len", b"data_len", "data_ptr", b"data_ptr", "destination_sids", b"destination_sids", "kind", b"kind", "local_participant_handle", b"local_participant_handle", "topic", b"topic"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_topic", b"_topic"]) -> typing_extensions.Literal["topic"] | None: ... + def HasField(self, field_name: typing.Literal["data_len", b"data_len", "data_ptr", b"data_ptr", "local_participant_handle", b"local_participant_handle", "reliable", b"reliable", "request_async_id", b"request_async_id", "topic", b"topic"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_len", b"data_len", "data_ptr", b"data_ptr", "destination_identities", b"destination_identities", "destination_sids", b"destination_sids", "local_participant_handle", b"local_participant_handle", "reliable", b"reliable", "request_async_id", b"request_async_id", "topic", b"topic"]) -> None: ... global___PublishDataRequest = PublishDataRequest -@typing_extensions.final +@typing.final class PublishDataResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -431,13 +664,14 @@ class PublishDataResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___PublishDataResponse = PublishDataResponse -@typing_extensions.final +@typing.final class PublishDataCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -448,16 +682,15 @@ class PublishDataCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... global___PublishDataCallback = PublishDataCallback -@typing_extensions.final +@typing.final class PublishTranscriptionRequest(google.protobuf.message.Message): """Publish transcription messages to room""" @@ -467,27 +700,28 @@ class PublishTranscriptionRequest(google.protobuf.message.Message): PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_ID_FIELD_NUMBER: builtins.int SEGMENTS_FIELD_NUMBER: builtins.int - LANGUAGE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int participant_identity: builtins.str track_id: builtins.str + request_async_id: builtins.int @property def segments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TranscriptionSegment]: ... - language: builtins.str def __init__( self, *, - local_participant_handle: builtins.int = ..., - participant_identity: builtins.str = ..., - track_id: builtins.str = ..., + local_participant_handle: builtins.int | None = ..., + participant_identity: builtins.str | None = ..., + track_id: builtins.str | None = ..., segments: collections.abc.Iterable[global___TranscriptionSegment] | None = ..., - language: builtins.str = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["language", b"language", "local_participant_handle", b"local_participant_handle", "participant_identity", b"participant_identity", "segments", b"segments", "track_id", b"track_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "participant_identity", b"participant_identity", "request_async_id", b"request_async_id", "track_id", b"track_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "participant_identity", b"participant_identity", "request_async_id", b"request_async_id", "segments", b"segments", "track_id", b"track_id"]) -> None: ... global___PublishTranscriptionRequest = PublishTranscriptionRequest -@typing_extensions.final +@typing.final class PublishTranscriptionResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -496,13 +730,14 @@ class PublishTranscriptionResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___PublishTranscriptionResponse = PublishTranscriptionResponse -@typing_extensions.final +@typing.final class PublishTranscriptionCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -513,37 +748,287 @@ class PublishTranscriptionCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... global___PublishTranscriptionCallback = PublishTranscriptionCallback -@typing_extensions.final -class UpdateLocalMetadataRequest(google.protobuf.message.Message): +@typing.final +class PublishSipDtmfRequest(google.protobuf.message.Message): + """Publish Sip DTMF messages to other participants""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + CODE_FIELD_NUMBER: builtins.int + DIGIT_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + code: builtins.int + digit: builtins.str + request_async_id: builtins.int + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + code: builtins.int | None = ..., + digit: builtins.str | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "digit", b"digit", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "destination_identities", b"destination_identities", "digit", b"digit", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id"]) -> None: ... + +global___PublishSipDtmfRequest = PublishSipDtmfRequest + +@typing.final +class PublishSipDtmfResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___PublishSipDtmfResponse = PublishSipDtmfResponse + +@typing.final +class PublishSipDtmfCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___PublishSipDtmfCallback = PublishSipDtmfCallback + +@typing.final +class SetLocalMetadataRequest(google.protobuf.message.Message): """Change the local participant's metadata""" DESCRIPTOR: google.protobuf.descriptor.Descriptor LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int METADATA_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int metadata: builtins.str + request_async_id: builtins.int + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + metadata: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "metadata", b"metadata", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "metadata", b"metadata", "request_async_id", b"request_async_id"]) -> None: ... + +global___SetLocalMetadataRequest = SetLocalMetadataRequest + +@typing.final +class SetLocalMetadataResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___SetLocalMetadataResponse = SetLocalMetadataResponse + +@typing.final +class SetLocalMetadataCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___SetLocalMetadataCallback = SetLocalMetadataCallback + +@typing.final +class SendChatMessageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + message: builtins.str + sender_identity: builtins.str + request_async_id: builtins.int + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + message: builtins.str | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + sender_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "message", b"message", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["destination_identities", b"destination_identities", "local_participant_handle", b"local_participant_handle", "message", b"message", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> None: ... + +global___SendChatMessageRequest = SendChatMessageRequest + +@typing.final +class EditChatMessageRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + EDIT_TEXT_FIELD_NUMBER: builtins.int + ORIGINAL_MESSAGE_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + edit_text: builtins.str + sender_identity: builtins.str + request_async_id: builtins.int + @property + def original_message(self) -> global___ChatMessage: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + edit_text: builtins.str | None = ..., + original_message: global___ChatMessage | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + sender_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["edit_text", b"edit_text", "local_participant_handle", b"local_participant_handle", "original_message", b"original_message", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["destination_identities", b"destination_identities", "edit_text", b"edit_text", "local_participant_handle", b"local_participant_handle", "original_message", b"original_message", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> None: ... + +global___EditChatMessageRequest = EditChatMessageRequest + +@typing.final +class SendChatMessageResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int def __init__( self, *, - local_participant_handle: builtins.int = ..., - metadata: builtins.str = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["local_participant_handle", b"local_participant_handle", "metadata", b"metadata"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... -global___UpdateLocalMetadataRequest = UpdateLocalMetadataRequest +global___SendChatMessageResponse = SendChatMessageResponse -@typing_extensions.final -class UpdateLocalMetadataResponse(google.protobuf.message.Message): +@typing.final +class SendChatMessageCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + CHAT_MESSAGE_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + @property + def chat_message(self) -> global___ChatMessage: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + chat_message: global___ChatMessage | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "chat_message", b"chat_message", "error", b"error", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "chat_message", b"chat_message", "error", b"error", "message", b"message"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["error", "chat_message"] | None: ... + +global___SendChatMessageCallback = SendChatMessageCallback + +@typing.final +class SetLocalAttributesRequest(google.protobuf.message.Message): + """Change the local participant's attributes""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + request_async_id: builtins.int + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AttributesEntry]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + attributes: collections.abc.Iterable[global___AttributesEntry] | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id"]) -> None: ... + +global___SetLocalAttributesRequest = SetLocalAttributesRequest + +@typing.final +class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + +global___AttributesEntry = AttributesEntry + +@typing.final +class SetLocalAttributesResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ASYNC_ID_FIELD_NUMBER: builtins.int @@ -551,49 +1036,58 @@ class UpdateLocalMetadataResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... -global___UpdateLocalMetadataResponse = UpdateLocalMetadataResponse +global___SetLocalAttributesResponse = SetLocalAttributesResponse -@typing_extensions.final -class UpdateLocalMetadataCallback(google.protobuf.message.Message): +@typing.final +class SetLocalAttributesCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int async_id: builtins.int + error: builtins.str def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... -global___UpdateLocalMetadataCallback = UpdateLocalMetadataCallback +global___SetLocalAttributesCallback = SetLocalAttributesCallback -@typing_extensions.final -class UpdateLocalNameRequest(google.protobuf.message.Message): +@typing.final +class SetLocalNameRequest(google.protobuf.message.Message): """Change the local participant's name""" DESCRIPTOR: google.protobuf.descriptor.Descriptor LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int local_participant_handle: builtins.int name: builtins.str + request_async_id: builtins.int def __init__( self, *, - local_participant_handle: builtins.int = ..., - name: builtins.str = ..., + local_participant_handle: builtins.int | None = ..., + name: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["local_participant_handle", b"local_participant_handle", "name", b"name"]) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "name", b"name", "request_async_id", b"request_async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "name", b"name", "request_async_id", b"request_async_id"]) -> None: ... -global___UpdateLocalNameRequest = UpdateLocalNameRequest +global___SetLocalNameRequest = SetLocalNameRequest -@typing_extensions.final -class UpdateLocalNameResponse(google.protobuf.message.Message): +@typing.final +class SetLocalNameResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ASYNC_ID_FIELD_NUMBER: builtins.int @@ -601,28 +1095,33 @@ class UpdateLocalNameResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... -global___UpdateLocalNameResponse = UpdateLocalNameResponse +global___SetLocalNameResponse = SetLocalNameResponse -@typing_extensions.final -class UpdateLocalNameCallback(google.protobuf.message.Message): +@typing.final +class SetLocalNameCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int async_id: builtins.int + error: builtins.str def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... -global___UpdateLocalNameCallback = UpdateLocalNameCallback +global___SetLocalNameCallback = SetLocalNameCallback -@typing_extensions.final +@typing.final class SetSubscribedRequest(google.protobuf.message.Message): """Change the "desire" to subs2ribe to a track""" @@ -635,14 +1134,15 @@ class SetSubscribedRequest(google.protobuf.message.Message): def __init__( self, *, - subscribe: builtins.bool = ..., - publication_handle: builtins.int = ..., + subscribe: builtins.bool | None = ..., + publication_handle: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["publication_handle", b"publication_handle", "subscribe", b"subscribe"]) -> None: ... + def HasField(self, field_name: typing.Literal["publication_handle", b"publication_handle", "subscribe", b"subscribe"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["publication_handle", b"publication_handle", "subscribe", b"subscribe"]) -> None: ... global___SetSubscribedRequest = SetSubscribedRequest -@typing_extensions.final +@typing.final class SetSubscribedResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -652,22 +1152,26 @@ class SetSubscribedResponse(google.protobuf.message.Message): global___SetSubscribedResponse = SetSubscribedResponse -@typing_extensions.final +@typing.final class GetSessionStatsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor ROOM_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int room_handle: builtins.int + request_async_id: builtins.int def __init__( self, *, - room_handle: builtins.int = ..., + room_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["room_handle", b"room_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "room_handle", b"room_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "room_handle", b"room_handle"]) -> None: ... global___GetSessionStatsRequest = GetSessionStatsRequest -@typing_extensions.final +@typing.final class GetSessionStatsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -676,41 +1180,56 @@ class GetSessionStatsResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___GetSessionStatsResponse = GetSessionStatsResponse -@typing_extensions.final +@typing.final class GetSessionStatsCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + @typing.final + class Result(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PUBLISHER_STATS_FIELD_NUMBER: builtins.int + SUBSCRIBER_STATS_FIELD_NUMBER: builtins.int + @property + def publisher_stats(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[stats_pb2.RtcStats]: ... + @property + def subscriber_stats(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[stats_pb2.RtcStats]: ... + def __init__( + self, + *, + publisher_stats: collections.abc.Iterable[stats_pb2.RtcStats] | None = ..., + subscriber_stats: collections.abc.Iterable[stats_pb2.RtcStats] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["publisher_stats", b"publisher_stats", "subscriber_stats", b"subscriber_stats"]) -> None: ... + ASYNC_ID_FIELD_NUMBER: builtins.int ERROR_FIELD_NUMBER: builtins.int - PUBLISHER_STATS_FIELD_NUMBER: builtins.int - SUBSCRIBER_STATS_FIELD_NUMBER: builtins.int + RESULT_FIELD_NUMBER: builtins.int async_id: builtins.int error: builtins.str @property - def publisher_stats(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[stats_pb2.RtcStats]: ... - @property - def subscriber_stats(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[stats_pb2.RtcStats]: ... + def result(self) -> global___GetSessionStatsCallback.Result: ... def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., - publisher_stats: collections.abc.Iterable[stats_pb2.RtcStats] | None = ..., - subscriber_stats: collections.abc.Iterable[stats_pb2.RtcStats] | None = ..., + result: global___GetSessionStatsCallback.Result | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error", "publisher_stats", b"publisher_stats", "subscriber_stats", b"subscriber_stats"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "result", b"result"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "message", b"message", "result", b"result"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["error", "result"] | None: ... global___GetSessionStatsCallback = GetSessionStatsCallback -@typing_extensions.final +@typing.final class VideoEncoding(google.protobuf.message.Message): """ Options @@ -725,14 +1244,15 @@ class VideoEncoding(google.protobuf.message.Message): def __init__( self, *, - max_bitrate: builtins.int = ..., - max_framerate: builtins.float = ..., + max_bitrate: builtins.int | None = ..., + max_framerate: builtins.float | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["max_bitrate", b"max_bitrate", "max_framerate", b"max_framerate"]) -> None: ... + def HasField(self, field_name: typing.Literal["max_bitrate", b"max_bitrate", "max_framerate", b"max_framerate"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["max_bitrate", b"max_bitrate", "max_framerate", b"max_framerate"]) -> None: ... global___VideoEncoding = VideoEncoding -@typing_extensions.final +@typing.final class AudioEncoding(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -741,13 +1261,14 @@ class AudioEncoding(google.protobuf.message.Message): def __init__( self, *, - max_bitrate: builtins.int = ..., + max_bitrate: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["max_bitrate", b"max_bitrate"]) -> None: ... + def HasField(self, field_name: typing.Literal["max_bitrate", b"max_bitrate"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["max_bitrate", b"max_bitrate"]) -> None: ... global___AudioEncoding = AudioEncoding -@typing_extensions.final +@typing.final class TrackPublishOptions(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -758,55 +1279,84 @@ class TrackPublishOptions(google.protobuf.message.Message): RED_FIELD_NUMBER: builtins.int SIMULCAST_FIELD_NUMBER: builtins.int SOURCE_FIELD_NUMBER: builtins.int - @property - def video_encoding(self) -> global___VideoEncoding: - """encodings are optional""" - @property - def audio_encoding(self) -> global___AudioEncoding: ... + STREAM_FIELD_NUMBER: builtins.int + PRECONNECT_BUFFER_FIELD_NUMBER: builtins.int + FRAME_METADATA_FEATURES_FIELD_NUMBER: builtins.int + SCALABILITY_MODE_FIELD_NUMBER: builtins.int + VIDEO_ENCODER_FIELD_NUMBER: builtins.int + DEGRADATION_PREFERENCE_FIELD_NUMBER: builtins.int video_codec: video_frame_pb2.VideoCodec.ValueType dtx: builtins.bool red: builtins.bool simulcast: builtins.bool source: track_pb2.TrackSource.ValueType + stream: builtins.str + preconnect_buffer: builtins.bool + scalability_mode: builtins.str + """RTP scalability mode (e.g. "L3T3_KEY"). When set, a single RTP + encoding is produced with this mode, enabling true SVC for codecs + that support it (VP9, AV1). Has no effect for VP8/H264. + """ + video_encoder: global___VideoEncoderBackend.ValueType + """Preferred encoder backend to use when publishing a video track.""" + degradation_preference: global___DegradationPreference.ValueType + """Controls how the encoder trades off between resolution and framerate + when bandwidth is constrained. Default is MAINTAIN_RESOLUTION. + """ + @property + def video_encoding(self) -> global___VideoEncoding: + """encodings are optional""" + + @property + def audio_encoding(self) -> global___AudioEncoding: ... + @property + def frame_metadata_features(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[track_pb2.FrameMetadataFeature.ValueType]: ... def __init__( self, *, video_encoding: global___VideoEncoding | None = ..., audio_encoding: global___AudioEncoding | None = ..., - video_codec: video_frame_pb2.VideoCodec.ValueType = ..., - dtx: builtins.bool = ..., - red: builtins.bool = ..., - simulcast: builtins.bool = ..., - source: track_pb2.TrackSource.ValueType = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["audio_encoding", b"audio_encoding", "video_encoding", b"video_encoding"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["audio_encoding", b"audio_encoding", "dtx", b"dtx", "red", b"red", "simulcast", b"simulcast", "source", b"source", "video_codec", b"video_codec", "video_encoding", b"video_encoding"]) -> None: ... + video_codec: video_frame_pb2.VideoCodec.ValueType | None = ..., + dtx: builtins.bool | None = ..., + red: builtins.bool | None = ..., + simulcast: builtins.bool | None = ..., + source: track_pb2.TrackSource.ValueType | None = ..., + stream: builtins.str | None = ..., + preconnect_buffer: builtins.bool | None = ..., + frame_metadata_features: collections.abc.Iterable[track_pb2.FrameMetadataFeature.ValueType] | None = ..., + scalability_mode: builtins.str | None = ..., + video_encoder: global___VideoEncoderBackend.ValueType | None = ..., + degradation_preference: global___DegradationPreference.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["audio_encoding", b"audio_encoding", "degradation_preference", b"degradation_preference", "dtx", b"dtx", "preconnect_buffer", b"preconnect_buffer", "red", b"red", "scalability_mode", b"scalability_mode", "simulcast", b"simulcast", "source", b"source", "stream", b"stream", "video_codec", b"video_codec", "video_encoder", b"video_encoder", "video_encoding", b"video_encoding"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_encoding", b"audio_encoding", "degradation_preference", b"degradation_preference", "dtx", b"dtx", "frame_metadata_features", b"frame_metadata_features", "preconnect_buffer", b"preconnect_buffer", "red", b"red", "scalability_mode", b"scalability_mode", "simulcast", b"simulcast", "source", b"source", "stream", b"stream", "video_codec", b"video_codec", "video_encoder", b"video_encoder", "video_encoding", b"video_encoding"]) -> None: ... global___TrackPublishOptions = TrackPublishOptions -@typing_extensions.final +@typing.final class IceServer(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor URLS_FIELD_NUMBER: builtins.int USERNAME_FIELD_NUMBER: builtins.int PASSWORD_FIELD_NUMBER: builtins.int - @property - def urls(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... username: builtins.str password: builtins.str + @property + def urls(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... def __init__( self, *, urls: collections.abc.Iterable[builtins.str] | None = ..., - username: builtins.str = ..., - password: builtins.str = ..., + username: builtins.str | None = ..., + password: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["password", b"password", "urls", b"urls", "username", b"username"]) -> None: ... + def HasField(self, field_name: typing.Literal["password", b"password", "username", b"username"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["password", b"password", "urls", b"urls", "username", b"username"]) -> None: ... global___IceServer = IceServer -@typing_extensions.final +@typing.final class RtcConfig(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -818,6 +1368,7 @@ class RtcConfig(google.protobuf.message.Message): @property def ice_servers(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___IceServer]: """empty fallback to default""" + def __init__( self, *, @@ -825,16 +1376,45 @@ class RtcConfig(google.protobuf.message.Message): continual_gathering_policy: global___ContinualGatheringPolicy.ValueType | None = ..., ice_servers: collections.abc.Iterable[global___IceServer] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_continual_gathering_policy", b"_continual_gathering_policy", "_ice_transport_type", b"_ice_transport_type", "continual_gathering_policy", b"continual_gathering_policy", "ice_transport_type", b"ice_transport_type"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_continual_gathering_policy", b"_continual_gathering_policy", "_ice_transport_type", b"_ice_transport_type", "continual_gathering_policy", b"continual_gathering_policy", "ice_servers", b"ice_servers", "ice_transport_type", b"ice_transport_type"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_continual_gathering_policy", b"_continual_gathering_policy"]) -> typing_extensions.Literal["continual_gathering_policy"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_ice_transport_type", b"_ice_transport_type"]) -> typing_extensions.Literal["ice_transport_type"] | None: ... + def HasField(self, field_name: typing.Literal["continual_gathering_policy", b"continual_gathering_policy", "ice_transport_type", b"ice_transport_type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["continual_gathering_policy", b"continual_gathering_policy", "ice_servers", b"ice_servers", "ice_transport_type", b"ice_transport_type"]) -> None: ... global___RtcConfig = RtcConfig -@typing_extensions.final +@typing.final +class RoomDataStreamOptions(google.protobuf.message.Message): + """Options controlling data stream behavior for the room.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MAX_PAYLOAD_BYTE_LENGTH_FIELD_NUMBER: builtins.int + USE_LEGACY_CLIENT_IMPLEMENTATION_FIELD_NUMBER: builtins.int + max_payload_byte_length: builtins.int + """Maximum decompressed payload size in bytes accepted for a single incoming + data stream. Streams exceeding this limit terminate with an error on the + receiving side. Unset falls back to the SDK default. + """ + use_legacy_client_implementation: builtins.bool + """Temporary migration aid for SDKs that still implement data streams in + their own client-side code on top of the FFI instead of using the FFI + data stream API. When true, this client does not advertise data streams + v2 support: ClientInfo.client_protocol is capped at 1 (DATA_STREAM_RPC), + so remote clients will not use v2-only behavior toward this client. + Defaults to false (v2 advertised). Will be removed once all FFI + consumers migrate. + """ + def __init__( + self, + *, + max_payload_byte_length: builtins.int | None = ..., + use_legacy_client_implementation: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["max_payload_byte_length", b"max_payload_byte_length", "use_legacy_client_implementation", b"use_legacy_client_implementation"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["max_payload_byte_length", b"max_payload_byte_length", "use_legacy_client_implementation", b"use_legacy_client_implementation"]) -> None: ... + +global___RoomDataStreamOptions = RoomDataStreamOptions + +@typing.final class RoomOptions(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -844,35 +1424,50 @@ class RoomOptions(google.protobuf.message.Message): E2EE_FIELD_NUMBER: builtins.int RTC_CONFIG_FIELD_NUMBER: builtins.int JOIN_RETRIES_FIELD_NUMBER: builtins.int + ENCRYPTION_FIELD_NUMBER: builtins.int + SINGLE_PEER_CONNECTION_FIELD_NUMBER: builtins.int + CONNECT_TIMEOUT_MS_FIELD_NUMBER: builtins.int + DATA_STREAM_FIELD_NUMBER: builtins.int auto_subscribe: builtins.bool adaptive_stream: builtins.bool dynacast: builtins.bool + join_retries: builtins.int + single_peer_connection: builtins.bool + """use single peer connection for both publish/subscribe (default: false)""" + connect_timeout_ms: builtins.int + """timeout in milliseconds for each signal connection attempt (default: 5000)""" @property def e2ee(self) -> e2ee_pb2.E2eeOptions: ... @property def rtc_config(self) -> global___RtcConfig: """allow to setup a custom RtcConfiguration""" - join_retries: builtins.int + + @property + def encryption(self) -> e2ee_pb2.E2eeOptions: ... + @property + def data_stream(self) -> global___RoomDataStreamOptions: + """data stream behavior for this room""" + def __init__( self, *, - auto_subscribe: builtins.bool = ..., - adaptive_stream: builtins.bool = ..., - dynacast: builtins.bool = ..., + auto_subscribe: builtins.bool | None = ..., + adaptive_stream: builtins.bool | None = ..., + dynacast: builtins.bool | None = ..., e2ee: e2ee_pb2.E2eeOptions | None = ..., rtc_config: global___RtcConfig | None = ..., - join_retries: builtins.int = ..., + join_retries: builtins.int | None = ..., + encryption: e2ee_pb2.E2eeOptions | None = ..., + single_peer_connection: builtins.bool | None = ..., + connect_timeout_ms: builtins.int | None = ..., + data_stream: global___RoomDataStreamOptions | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_e2ee", b"_e2ee", "_rtc_config", b"_rtc_config", "e2ee", b"e2ee", "rtc_config", b"rtc_config"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_e2ee", b"_e2ee", "_rtc_config", b"_rtc_config", "adaptive_stream", b"adaptive_stream", "auto_subscribe", b"auto_subscribe", "dynacast", b"dynacast", "e2ee", b"e2ee", "join_retries", b"join_retries", "rtc_config", b"rtc_config"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_e2ee", b"_e2ee"]) -> typing_extensions.Literal["e2ee"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_rtc_config", b"_rtc_config"]) -> typing_extensions.Literal["rtc_config"] | None: ... + def HasField(self, field_name: typing.Literal["adaptive_stream", b"adaptive_stream", "auto_subscribe", b"auto_subscribe", "connect_timeout_ms", b"connect_timeout_ms", "data_stream", b"data_stream", "dynacast", b"dynacast", "e2ee", b"e2ee", "encryption", b"encryption", "join_retries", b"join_retries", "rtc_config", b"rtc_config", "single_peer_connection", b"single_peer_connection"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["adaptive_stream", b"adaptive_stream", "auto_subscribe", b"auto_subscribe", "connect_timeout_ms", b"connect_timeout_ms", "data_stream", b"data_stream", "dynacast", b"dynacast", "e2ee", b"e2ee", "encryption", b"encryption", "join_retries", b"join_retries", "rtc_config", b"rtc_config", "single_peer_connection", b"single_peer_connection"]) -> None: ... global___RoomOptions = RoomOptions -@typing_extensions.final +@typing.final class TranscriptionSegment(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -881,25 +1476,29 @@ class TranscriptionSegment(google.protobuf.message.Message): START_TIME_FIELD_NUMBER: builtins.int END_TIME_FIELD_NUMBER: builtins.int FINAL_FIELD_NUMBER: builtins.int + LANGUAGE_FIELD_NUMBER: builtins.int id: builtins.str text: builtins.str start_time: builtins.int end_time: builtins.int final: builtins.bool + language: builtins.str def __init__( self, *, - id: builtins.str = ..., - text: builtins.str = ..., - start_time: builtins.int = ..., - end_time: builtins.int = ..., - final: builtins.bool = ..., + id: builtins.str | None = ..., + text: builtins.str | None = ..., + start_time: builtins.int | None = ..., + end_time: builtins.int | None = ..., + final: builtins.bool | None = ..., + language: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["end_time", b"end_time", "final", b"final", "id", b"id", "start_time", b"start_time", "text", b"text"]) -> None: ... + def HasField(self, field_name: typing.Literal["end_time", b"end_time", "final", b"final", "id", b"id", "language", b"language", "start_time", b"start_time", "text", b"text"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["end_time", b"end_time", "final", b"final", "id", b"id", "language", b"language", "start_time", b"start_time", "text", b"text"]) -> None: ... global___TranscriptionSegment = TranscriptionSegment -@typing_extensions.final +@typing.final class BufferInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -910,14 +1509,15 @@ class BufferInfo(google.protobuf.message.Message): def __init__( self, *, - data_ptr: builtins.int = ..., - data_len: builtins.int = ..., + data_ptr: builtins.int | None = ..., + data_len: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["data_len", b"data_len", "data_ptr", b"data_ptr"]) -> None: ... + def HasField(self, field_name: typing.Literal["data_len", b"data_len", "data_ptr", b"data_ptr"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_len", b"data_len", "data_ptr", b"data_ptr"]) -> None: ... global___BufferInfo = BufferInfo -@typing_extensions.final +@typing.final class OwnedBuffer(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -933,12 +1533,12 @@ class OwnedBuffer(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., data: global___BufferInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["data", b"data", "handle", b"handle"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["data", b"data", "handle", b"handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["data", b"data", "handle", b"handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "handle", b"handle"]) -> None: ... global___OwnedBuffer = OwnedBuffer -@typing_extensions.final +@typing.final class RoomEvent(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -947,6 +1547,7 @@ class RoomEvent(google.protobuf.message.Message): PARTICIPANT_DISCONNECTED_FIELD_NUMBER: builtins.int LOCAL_TRACK_PUBLISHED_FIELD_NUMBER: builtins.int LOCAL_TRACK_UNPUBLISHED_FIELD_NUMBER: builtins.int + LOCAL_TRACK_SUBSCRIBED_FIELD_NUMBER: builtins.int TRACK_PUBLISHED_FIELD_NUMBER: builtins.int TRACK_UNPUBLISHED_FIELD_NUMBER: builtins.int TRACK_SUBSCRIBED_FIELD_NUMBER: builtins.int @@ -956,8 +1557,10 @@ class RoomEvent(google.protobuf.message.Message): TRACK_UNMUTED_FIELD_NUMBER: builtins.int ACTIVE_SPEAKERS_CHANGED_FIELD_NUMBER: builtins.int ROOM_METADATA_CHANGED_FIELD_NUMBER: builtins.int + ROOM_SID_CHANGED_FIELD_NUMBER: builtins.int PARTICIPANT_METADATA_CHANGED_FIELD_NUMBER: builtins.int PARTICIPANT_NAME_CHANGED_FIELD_NUMBER: builtins.int + PARTICIPANT_ATTRIBUTES_CHANGED_FIELD_NUMBER: builtins.int CONNECTION_QUALITY_CHANGED_FIELD_NUMBER: builtins.int CONNECTION_STATE_CHANGED_FIELD_NUMBER: builtins.int DISCONNECTED_FIELD_NUMBER: builtins.int @@ -966,6 +1569,24 @@ class RoomEvent(google.protobuf.message.Message): E2EE_STATE_CHANGED_FIELD_NUMBER: builtins.int EOS_FIELD_NUMBER: builtins.int DATA_PACKET_RECEIVED_FIELD_NUMBER: builtins.int + TRANSCRIPTION_RECEIVED_FIELD_NUMBER: builtins.int + CHAT_MESSAGE_FIELD_NUMBER: builtins.int + STREAM_HEADER_RECEIVED_FIELD_NUMBER: builtins.int + STREAM_CHUNK_RECEIVED_FIELD_NUMBER: builtins.int + STREAM_TRAILER_RECEIVED_FIELD_NUMBER: builtins.int + DATA_CHANNEL_LOW_THRESHOLD_CHANGED_FIELD_NUMBER: builtins.int + BYTE_STREAM_OPENED_FIELD_NUMBER: builtins.int + TEXT_STREAM_OPENED_FIELD_NUMBER: builtins.int + ROOM_UPDATED_FIELD_NUMBER: builtins.int + MOVED_FIELD_NUMBER: builtins.int + PARTICIPANTS_UPDATED_FIELD_NUMBER: builtins.int + PARTICIPANT_ENCRYPTION_STATUS_CHANGED_FIELD_NUMBER: builtins.int + PARTICIPANT_PERMISSION_CHANGED_FIELD_NUMBER: builtins.int + TOKEN_REFRESHED_FIELD_NUMBER: builtins.int + PARTICIPANT_ACTIVE_FIELD_NUMBER: builtins.int + DATA_TRACK_PUBLISHED_FIELD_NUMBER: builtins.int + DATA_TRACK_UNPUBLISHED_FIELD_NUMBER: builtins.int + LOCAL_TRACK_REPUBLISHED_FIELD_NUMBER: builtins.int room_handle: builtins.int @property def participant_connected(self) -> global___ParticipantConnected: ... @@ -976,6 +1597,8 @@ class RoomEvent(google.protobuf.message.Message): @property def local_track_unpublished(self) -> global___LocalTrackUnpublished: ... @property + def local_track_subscribed(self) -> global___LocalTrackSubscribed: ... + @property def track_published(self) -> global___TrackPublished: ... @property def track_unpublished(self) -> global___TrackUnpublished: ... @@ -994,16 +1617,21 @@ class RoomEvent(google.protobuf.message.Message): @property def room_metadata_changed(self) -> global___RoomMetadataChanged: ... @property + def room_sid_changed(self) -> global___RoomSidChanged: ... + @property def participant_metadata_changed(self) -> global___ParticipantMetadataChanged: ... @property def participant_name_changed(self) -> global___ParticipantNameChanged: ... @property + def participant_attributes_changed(self) -> global___ParticipantAttributesChanged: ... + @property def connection_quality_changed(self) -> global___ConnectionQualityChanged: ... @property def connection_state_changed(self) -> global___ConnectionStateChanged: ... @property def disconnected(self) -> global___Disconnected: - """Connected connected = 20;""" + """Connected connected = 21;""" + @property def reconnecting(self) -> global___Reconnecting: ... @property @@ -1013,16 +1641,64 @@ class RoomEvent(google.protobuf.message.Message): @property def eos(self) -> global___RoomEOS: """The stream of room events has ended""" + @property def data_packet_received(self) -> global___DataPacketReceived: ... + @property + def transcription_received(self) -> global___TranscriptionReceived: ... + @property + def chat_message(self) -> global___ChatMessageReceived: ... + @property + def stream_header_received(self) -> global___DataStreamHeaderReceived: + """Data stream (low level)""" + + @property + def stream_chunk_received(self) -> global___DataStreamChunkReceived: ... + @property + def stream_trailer_received(self) -> global___DataStreamTrailerReceived: ... + @property + def data_channel_low_threshold_changed(self) -> global___DataChannelBufferedAmountLowThresholdChanged: ... + @property + def byte_stream_opened(self) -> global___ByteStreamOpened: + """Data stream (high level)""" + + @property + def text_stream_opened(self) -> global___TextStreamOpened: ... + @property + def room_updated(self) -> global___RoomInfo: + """Room info updated""" + + @property + def moved(self) -> global___RoomInfo: + """Participant moved to new room""" + + @property + def participants_updated(self) -> global___ParticipantsUpdated: + """carry over all participant info updates, including sid""" + + @property + def participant_encryption_status_changed(self) -> global___ParticipantEncryptionStatusChanged: ... + @property + def participant_permission_changed(self) -> global___ParticipantPermissionChanged: ... + @property + def token_refreshed(self) -> global___TokenRefreshed: ... + @property + def participant_active(self) -> global___ParticipantActive: ... + @property + def data_track_published(self) -> global___DataTrackPublished: ... + @property + def data_track_unpublished(self) -> global___DataTrackUnpublished: ... + @property + def local_track_republished(self) -> global___LocalTrackRepublished: ... def __init__( self, *, - room_handle: builtins.int = ..., + room_handle: builtins.int | None = ..., participant_connected: global___ParticipantConnected | None = ..., participant_disconnected: global___ParticipantDisconnected | None = ..., local_track_published: global___LocalTrackPublished | None = ..., local_track_unpublished: global___LocalTrackUnpublished | None = ..., + local_track_subscribed: global___LocalTrackSubscribed | None = ..., track_published: global___TrackPublished | None = ..., track_unpublished: global___TrackUnpublished | None = ..., track_subscribed: global___TrackSubscribed | None = ..., @@ -1032,8 +1708,10 @@ class RoomEvent(google.protobuf.message.Message): track_unmuted: global___TrackUnmuted | None = ..., active_speakers_changed: global___ActiveSpeakersChanged | None = ..., room_metadata_changed: global___RoomMetadataChanged | None = ..., + room_sid_changed: global___RoomSidChanged | None = ..., participant_metadata_changed: global___ParticipantMetadataChanged | None = ..., participant_name_changed: global___ParticipantNameChanged | None = ..., + participant_attributes_changed: global___ParticipantAttributesChanged | None = ..., connection_quality_changed: global___ConnectionQualityChanged | None = ..., connection_state_changed: global___ConnectionStateChanged | None = ..., disconnected: global___Disconnected | None = ..., @@ -1042,35 +1720,81 @@ class RoomEvent(google.protobuf.message.Message): e2ee_state_changed: global___E2eeStateChanged | None = ..., eos: global___RoomEOS | None = ..., data_packet_received: global___DataPacketReceived | None = ..., - ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["active_speakers_changed", b"active_speakers_changed", "connection_quality_changed", b"connection_quality_changed", "connection_state_changed", b"connection_state_changed", "data_packet_received", b"data_packet_received", "disconnected", b"disconnected", "e2ee_state_changed", b"e2ee_state_changed", "eos", b"eos", "local_track_published", b"local_track_published", "local_track_unpublished", b"local_track_unpublished", "message", b"message", "participant_connected", b"participant_connected", "participant_disconnected", b"participant_disconnected", "participant_metadata_changed", b"participant_metadata_changed", "participant_name_changed", b"participant_name_changed", "reconnected", b"reconnected", "reconnecting", b"reconnecting", "room_metadata_changed", b"room_metadata_changed", "track_muted", b"track_muted", "track_published", b"track_published", "track_subscribed", b"track_subscribed", "track_subscription_failed", b"track_subscription_failed", "track_unmuted", b"track_unmuted", "track_unpublished", b"track_unpublished", "track_unsubscribed", b"track_unsubscribed"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["active_speakers_changed", b"active_speakers_changed", "connection_quality_changed", b"connection_quality_changed", "connection_state_changed", b"connection_state_changed", "data_packet_received", b"data_packet_received", "disconnected", b"disconnected", "e2ee_state_changed", b"e2ee_state_changed", "eos", b"eos", "local_track_published", b"local_track_published", "local_track_unpublished", b"local_track_unpublished", "message", b"message", "participant_connected", b"participant_connected", "participant_disconnected", b"participant_disconnected", "participant_metadata_changed", b"participant_metadata_changed", "participant_name_changed", b"participant_name_changed", "reconnected", b"reconnected", "reconnecting", b"reconnecting", "room_handle", b"room_handle", "room_metadata_changed", b"room_metadata_changed", "track_muted", b"track_muted", "track_published", b"track_published", "track_subscribed", b"track_subscribed", "track_subscription_failed", b"track_subscription_failed", "track_unmuted", b"track_unmuted", "track_unpublished", b"track_unpublished", "track_unsubscribed", b"track_unsubscribed"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["participant_connected", "participant_disconnected", "local_track_published", "local_track_unpublished", "track_published", "track_unpublished", "track_subscribed", "track_unsubscribed", "track_subscription_failed", "track_muted", "track_unmuted", "active_speakers_changed", "room_metadata_changed", "participant_metadata_changed", "participant_name_changed", "connection_quality_changed", "connection_state_changed", "disconnected", "reconnecting", "reconnected", "e2ee_state_changed", "eos", "data_packet_received"] | None: ... + transcription_received: global___TranscriptionReceived | None = ..., + chat_message: global___ChatMessageReceived | None = ..., + stream_header_received: global___DataStreamHeaderReceived | None = ..., + stream_chunk_received: global___DataStreamChunkReceived | None = ..., + stream_trailer_received: global___DataStreamTrailerReceived | None = ..., + data_channel_low_threshold_changed: global___DataChannelBufferedAmountLowThresholdChanged | None = ..., + byte_stream_opened: global___ByteStreamOpened | None = ..., + text_stream_opened: global___TextStreamOpened | None = ..., + room_updated: global___RoomInfo | None = ..., + moved: global___RoomInfo | None = ..., + participants_updated: global___ParticipantsUpdated | None = ..., + participant_encryption_status_changed: global___ParticipantEncryptionStatusChanged | None = ..., + participant_permission_changed: global___ParticipantPermissionChanged | None = ..., + token_refreshed: global___TokenRefreshed | None = ..., + participant_active: global___ParticipantActive | None = ..., + data_track_published: global___DataTrackPublished | None = ..., + data_track_unpublished: global___DataTrackUnpublished | None = ..., + local_track_republished: global___LocalTrackRepublished | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["active_speakers_changed", b"active_speakers_changed", "byte_stream_opened", b"byte_stream_opened", "chat_message", b"chat_message", "connection_quality_changed", b"connection_quality_changed", "connection_state_changed", b"connection_state_changed", "data_channel_low_threshold_changed", b"data_channel_low_threshold_changed", "data_packet_received", b"data_packet_received", "data_track_published", b"data_track_published", "data_track_unpublished", b"data_track_unpublished", "disconnected", b"disconnected", "e2ee_state_changed", b"e2ee_state_changed", "eos", b"eos", "local_track_published", b"local_track_published", "local_track_republished", b"local_track_republished", "local_track_subscribed", b"local_track_subscribed", "local_track_unpublished", b"local_track_unpublished", "message", b"message", "moved", b"moved", "participant_active", b"participant_active", "participant_attributes_changed", b"participant_attributes_changed", "participant_connected", b"participant_connected", "participant_disconnected", b"participant_disconnected", "participant_encryption_status_changed", b"participant_encryption_status_changed", "participant_metadata_changed", b"participant_metadata_changed", "participant_name_changed", b"participant_name_changed", "participant_permission_changed", b"participant_permission_changed", "participants_updated", b"participants_updated", "reconnected", b"reconnected", "reconnecting", b"reconnecting", "room_handle", b"room_handle", "room_metadata_changed", b"room_metadata_changed", "room_sid_changed", b"room_sid_changed", "room_updated", b"room_updated", "stream_chunk_received", b"stream_chunk_received", "stream_header_received", b"stream_header_received", "stream_trailer_received", b"stream_trailer_received", "text_stream_opened", b"text_stream_opened", "token_refreshed", b"token_refreshed", "track_muted", b"track_muted", "track_published", b"track_published", "track_subscribed", b"track_subscribed", "track_subscription_failed", b"track_subscription_failed", "track_unmuted", b"track_unmuted", "track_unpublished", b"track_unpublished", "track_unsubscribed", b"track_unsubscribed", "transcription_received", b"transcription_received"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["active_speakers_changed", b"active_speakers_changed", "byte_stream_opened", b"byte_stream_opened", "chat_message", b"chat_message", "connection_quality_changed", b"connection_quality_changed", "connection_state_changed", b"connection_state_changed", "data_channel_low_threshold_changed", b"data_channel_low_threshold_changed", "data_packet_received", b"data_packet_received", "data_track_published", b"data_track_published", "data_track_unpublished", b"data_track_unpublished", "disconnected", b"disconnected", "e2ee_state_changed", b"e2ee_state_changed", "eos", b"eos", "local_track_published", b"local_track_published", "local_track_republished", b"local_track_republished", "local_track_subscribed", b"local_track_subscribed", "local_track_unpublished", b"local_track_unpublished", "message", b"message", "moved", b"moved", "participant_active", b"participant_active", "participant_attributes_changed", b"participant_attributes_changed", "participant_connected", b"participant_connected", "participant_disconnected", b"participant_disconnected", "participant_encryption_status_changed", b"participant_encryption_status_changed", "participant_metadata_changed", b"participant_metadata_changed", "participant_name_changed", b"participant_name_changed", "participant_permission_changed", b"participant_permission_changed", "participants_updated", b"participants_updated", "reconnected", b"reconnected", "reconnecting", b"reconnecting", "room_handle", b"room_handle", "room_metadata_changed", b"room_metadata_changed", "room_sid_changed", b"room_sid_changed", "room_updated", b"room_updated", "stream_chunk_received", b"stream_chunk_received", "stream_header_received", b"stream_header_received", "stream_trailer_received", b"stream_trailer_received", "text_stream_opened", b"text_stream_opened", "token_refreshed", b"token_refreshed", "track_muted", b"track_muted", "track_published", b"track_published", "track_subscribed", b"track_subscribed", "track_subscription_failed", b"track_subscription_failed", "track_unmuted", b"track_unmuted", "track_unpublished", b"track_unpublished", "track_unsubscribed", b"track_unsubscribed", "transcription_received", b"transcription_received"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["participant_connected", "participant_disconnected", "local_track_published", "local_track_unpublished", "local_track_subscribed", "track_published", "track_unpublished", "track_subscribed", "track_unsubscribed", "track_subscription_failed", "track_muted", "track_unmuted", "active_speakers_changed", "room_metadata_changed", "room_sid_changed", "participant_metadata_changed", "participant_name_changed", "participant_attributes_changed", "connection_quality_changed", "connection_state_changed", "disconnected", "reconnecting", "reconnected", "e2ee_state_changed", "eos", "data_packet_received", "transcription_received", "chat_message", "stream_header_received", "stream_chunk_received", "stream_trailer_received", "data_channel_low_threshold_changed", "byte_stream_opened", "text_stream_opened", "room_updated", "moved", "participants_updated", "participant_encryption_status_changed", "participant_permission_changed", "token_refreshed", "participant_active", "data_track_published", "data_track_unpublished", "local_track_republished"] | None: ... global___RoomEvent = RoomEvent -@typing_extensions.final +@typing.final class RoomInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor SID_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int METADATA_FIELD_NUMBER: builtins.int + LOSSY_DC_BUFFERED_AMOUNT_LOW_THRESHOLD_FIELD_NUMBER: builtins.int + RELIABLE_DC_BUFFERED_AMOUNT_LOW_THRESHOLD_FIELD_NUMBER: builtins.int + EMPTY_TIMEOUT_FIELD_NUMBER: builtins.int + DEPARTURE_TIMEOUT_FIELD_NUMBER: builtins.int + MAX_PARTICIPANTS_FIELD_NUMBER: builtins.int + CREATION_TIME_FIELD_NUMBER: builtins.int + NUM_PARTICIPANTS_FIELD_NUMBER: builtins.int + NUM_PUBLISHERS_FIELD_NUMBER: builtins.int + ACTIVE_RECORDING_FIELD_NUMBER: builtins.int sid: builtins.str name: builtins.str metadata: builtins.str + lossy_dc_buffered_amount_low_threshold: builtins.int + reliable_dc_buffered_amount_low_threshold: builtins.int + empty_timeout: builtins.int + departure_timeout: builtins.int + max_participants: builtins.int + creation_time: builtins.int + num_participants: builtins.int + num_publishers: builtins.int + active_recording: builtins.bool def __init__( self, *, - sid: builtins.str = ..., - name: builtins.str = ..., - metadata: builtins.str = ..., - ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["metadata", b"metadata", "name", b"name", "sid", b"sid"]) -> None: ... + sid: builtins.str | None = ..., + name: builtins.str | None = ..., + metadata: builtins.str | None = ..., + lossy_dc_buffered_amount_low_threshold: builtins.int | None = ..., + reliable_dc_buffered_amount_low_threshold: builtins.int | None = ..., + empty_timeout: builtins.int | None = ..., + departure_timeout: builtins.int | None = ..., + max_participants: builtins.int | None = ..., + creation_time: builtins.int | None = ..., + num_participants: builtins.int | None = ..., + num_publishers: builtins.int | None = ..., + active_recording: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["active_recording", b"active_recording", "creation_time", b"creation_time", "departure_timeout", b"departure_timeout", "empty_timeout", b"empty_timeout", "lossy_dc_buffered_amount_low_threshold", b"lossy_dc_buffered_amount_low_threshold", "max_participants", b"max_participants", "metadata", b"metadata", "name", b"name", "num_participants", b"num_participants", "num_publishers", b"num_publishers", "reliable_dc_buffered_amount_low_threshold", b"reliable_dc_buffered_amount_low_threshold", "sid", b"sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["active_recording", b"active_recording", "creation_time", b"creation_time", "departure_timeout", b"departure_timeout", "empty_timeout", b"empty_timeout", "lossy_dc_buffered_amount_low_threshold", b"lossy_dc_buffered_amount_low_threshold", "max_participants", b"max_participants", "metadata", b"metadata", "name", b"name", "num_participants", b"num_participants", "num_publishers", b"num_publishers", "reliable_dc_buffered_amount_low_threshold", b"reliable_dc_buffered_amount_low_threshold", "sid", b"sid"]) -> None: ... global___RoomInfo = RoomInfo -@typing_extensions.final +@typing.final class OwnedRoom(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1086,12 +1810,28 @@ class OwnedRoom(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___RoomInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedRoom = OwnedRoom -@typing_extensions.final +@typing.final +class ParticipantsUpdated(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANTS_FIELD_NUMBER: builtins.int + @property + def participants(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[participant_pb2.ParticipantInfo]: ... + def __init__( + self, + *, + participants: collections.abc.Iterable[participant_pb2.ParticipantInfo] | None = ..., + ) -> None: ... + def ClearField(self, field_name: typing.Literal["participants", b"participants"]) -> None: ... + +global___ParticipantsUpdated = ParticipantsUpdated + +@typing.final class ParticipantConnected(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1103,27 +1843,47 @@ class ParticipantConnected(google.protobuf.message.Message): *, info: participant_pb2.OwnedParticipant | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["info", b"info"]) -> None: ... global___ParticipantConnected = ParticipantConnected -@typing_extensions.final +@typing.final +class ParticipantActive(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity"]) -> None: ... + +global___ParticipantActive = ParticipantActive + +@typing.final class ParticipantDisconnected(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + DISCONNECT_REASON_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + disconnect_reason: participant_pb2.DisconnectReason.ValueType def __init__( self, *, - participant_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., + disconnect_reason: participant_pb2.DisconnectReason.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["disconnect_reason", b"disconnect_reason", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["disconnect_reason", b"disconnect_reason", "participant_identity", b"participant_identity"]) -> None: ... global___ParticipantDisconnected = ParticipantDisconnected -@typing_extensions.final +@typing.final class LocalTrackPublished(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1135,13 +1895,14 @@ class LocalTrackPublished(google.protobuf.message.Message): def __init__( self, *, - track_sid: builtins.str = ..., + track_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track_sid", b"track_sid"]) -> None: ... global___LocalTrackPublished = LocalTrackPublished -@typing_extensions.final +@typing.final class LocalTrackUnpublished(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1150,51 +1911,101 @@ class LocalTrackUnpublished(google.protobuf.message.Message): def __init__( self, *, - publication_sid: builtins.str = ..., + publication_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["publication_sid", b"publication_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["publication_sid", b"publication_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["publication_sid", b"publication_sid"]) -> None: ... global___LocalTrackUnpublished = LocalTrackUnpublished -@typing_extensions.final +@typing.final +class LocalTrackRepublished(google.protobuf.message.Message): + """Fired when the SDK auto-republishes a local track during a full + reconnect. The FfiPublication handle is preserved across the cycle — + language bindings should look up the existing publication object by + `previous_sid` (its old SID), update its TrackPublicationInfo in place + with `info`, and rekey it under the new SID. Apps holding a cached + reference to the publication continue to see a valid object whose + reads/writes hit current state. + """ + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PUBLICATION_HANDLE_FIELD_NUMBER: builtins.int + PREVIOUS_SID_FIELD_NUMBER: builtins.int + INFO_FIELD_NUMBER: builtins.int + publication_handle: builtins.int + previous_sid: builtins.str + @property + def info(self) -> track_pb2.TrackPublicationInfo: ... + def __init__( + self, + *, + publication_handle: builtins.int | None = ..., + previous_sid: builtins.str | None = ..., + info: track_pb2.TrackPublicationInfo | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["info", b"info", "previous_sid", b"previous_sid", "publication_handle", b"publication_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["info", b"info", "previous_sid", b"previous_sid", "publication_handle", b"publication_handle"]) -> None: ... + +global___LocalTrackRepublished = LocalTrackRepublished + +@typing.final +class LocalTrackSubscribed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_SID_FIELD_NUMBER: builtins.int + track_sid: builtins.str + def __init__( + self, + *, + track_sid: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track_sid", b"track_sid"]) -> None: ... + +global___LocalTrackSubscribed = LocalTrackSubscribed + +@typing.final class TrackPublished(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int PUBLICATION_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str @property def publication(self) -> track_pb2.OwnedTrackPublication: ... def __init__( self, *, - participant_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., publication: track_pb2.OwnedTrackPublication | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["publication", b"publication"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "publication", b"publication"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "publication", b"publication"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "publication", b"publication"]) -> None: ... global___TrackPublished = TrackPublished -@typing_extensions.final +@typing.final class TrackUnpublished(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int PUBLICATION_SID_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str publication_sid: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - publication_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., + publication_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "publication_sid", b"publication_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "publication_sid", b"publication_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "publication_sid", b"publication_sid"]) -> None: ... global___TrackUnpublished = TrackUnpublished -@typing_extensions.final +@typing.final class TrackSubscribed(google.protobuf.message.Message): """Publication isn't needed for subscription events on the FFI The FFI will retrieve the publication using the Track sid @@ -1202,134 +2013,139 @@ class TrackSubscribed(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str @property def track(self) -> track_pb2.OwnedTrack: ... def __init__( self, *, - participant_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., track: track_pb2.OwnedTrack | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["track", b"track"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "track", b"track"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track", b"track"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track", b"track"]) -> None: ... global___TrackSubscribed = TrackSubscribed -@typing_extensions.final +@typing.final class TrackUnsubscribed(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_SID_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str """The FFI language can dispose/remove the VideoSink here""" track_sid: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - track_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___TrackUnsubscribed = TrackUnsubscribed -@typing_extensions.final +@typing.final class TrackSubscriptionFailed(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_SID_FIELD_NUMBER: builtins.int ERROR_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str track_sid: builtins.str error: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - track_sid: builtins.str = ..., - error: builtins.str = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., + error: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["error", b"error", "participant_sid", b"participant_sid", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___TrackSubscriptionFailed = TrackSubscriptionFailed -@typing_extensions.final +@typing.final class TrackMuted(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_SID_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str track_sid: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - track_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___TrackMuted = TrackMuted -@typing_extensions.final +@typing.final class TrackUnmuted(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int TRACK_SID_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str track_sid: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - track_sid: builtins.str = ..., + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "track_sid", b"track_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> None: ... global___TrackUnmuted = TrackUnmuted -@typing_extensions.final +@typing.final class E2eeStateChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int STATE_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str """Using sid instead of identity for ffi communication""" state: e2ee_pb2.EncryptionState.ValueType def __init__( self, *, - participant_sid: builtins.str = ..., - state: e2ee_pb2.EncryptionState.ValueType = ..., + participant_identity: builtins.str | None = ..., + state: e2ee_pb2.EncryptionState.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "state", b"state"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "state", b"state"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "state", b"state"]) -> None: ... global___E2eeStateChanged = E2eeStateChanged -@typing_extensions.final +@typing.final class ActiveSpeakersChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SIDS_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITIES_FIELD_NUMBER: builtins.int @property - def participant_sids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def participant_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... def __init__( self, *, - participant_sids: collections.abc.Iterable[builtins.str] | None = ..., + participant_identities: collections.abc.Iterable[builtins.str] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sids", b"participant_sids"]) -> None: ... + def ClearField(self, field_name: typing.Literal["participant_identities", b"participant_identities"]) -> None: ... global___ActiveSpeakersChanged = ActiveSpeakersChanged -@typing_extensions.final +@typing.final class RoomMetadataChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1338,88 +2154,221 @@ class RoomMetadataChanged(google.protobuf.message.Message): def __init__( self, *, - metadata: builtins.str = ..., + metadata: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["metadata", b"metadata"]) -> None: ... + def HasField(self, field_name: typing.Literal["metadata", b"metadata"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["metadata", b"metadata"]) -> None: ... global___RoomMetadataChanged = RoomMetadataChanged -@typing_extensions.final +@typing.final +class RoomSidChanged(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SID_FIELD_NUMBER: builtins.int + sid: builtins.str + def __init__( + self, + *, + sid: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["sid", b"sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["sid", b"sid"]) -> None: ... + +global___RoomSidChanged = RoomSidChanged + +@typing.final class ParticipantMetadataChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int METADATA_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str metadata: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - metadata: builtins.str = ..., + participant_identity: builtins.str | None = ..., + metadata: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["metadata", b"metadata", "participant_sid", b"participant_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["metadata", b"metadata", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["metadata", b"metadata", "participant_identity", b"participant_identity"]) -> None: ... global___ParticipantMetadataChanged = ParticipantMetadataChanged -@typing_extensions.final +@typing.final +class ParticipantAttributesChanged(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + CHANGED_ATTRIBUTES_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AttributesEntry]: ... + @property + def changed_attributes(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___AttributesEntry]: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + attributes: collections.abc.Iterable[global___AttributesEntry] | None = ..., + changed_attributes: collections.abc.Iterable[global___AttributesEntry] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "changed_attributes", b"changed_attributes", "participant_identity", b"participant_identity"]) -> None: ... + +global___ParticipantAttributesChanged = ParticipantAttributesChanged + +@typing.final +class ParticipantEncryptionStatusChanged(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + IS_ENCRYPTED_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + is_encrypted: builtins.bool + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + is_encrypted: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["is_encrypted", b"is_encrypted", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["is_encrypted", b"is_encrypted", "participant_identity", b"participant_identity"]) -> None: ... + +global___ParticipantEncryptionStatusChanged = ParticipantEncryptionStatusChanged + +@typing.final class ParticipantNameChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int NAME_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str name: builtins.str def __init__( self, *, - participant_sid: builtins.str = ..., - name: builtins.str = ..., + participant_identity: builtins.str | None = ..., + name: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["name", b"name", "participant_sid", b"participant_sid"]) -> None: ... + def HasField(self, field_name: typing.Literal["name", b"name", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["name", b"name", "participant_identity", b"participant_identity"]) -> None: ... global___ParticipantNameChanged = ParticipantNameChanged -@typing_extensions.final +@typing.final +class ParticipantPermissionChanged(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + PERMISSION_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def permission(self) -> participant_pb2.ParticipantPermission: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + permission: participant_pb2.ParticipantPermission | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "permission", b"permission"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "permission", b"permission"]) -> None: ... + +global___ParticipantPermissionChanged = ParticipantPermissionChanged + +@typing.final class ConnectionQualityChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - PARTICIPANT_SID_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int QUALITY_FIELD_NUMBER: builtins.int - participant_sid: builtins.str + participant_identity: builtins.str quality: global___ConnectionQuality.ValueType def __init__( self, *, - participant_sid: builtins.str = ..., - quality: global___ConnectionQuality.ValueType = ..., + participant_identity: builtins.str | None = ..., + quality: global___ConnectionQuality.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["participant_sid", b"participant_sid", "quality", b"quality"]) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "quality", b"quality"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "quality", b"quality"]) -> None: ... global___ConnectionQualityChanged = ConnectionQualityChanged -@typing_extensions.final +@typing.final class UserPacket(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor DATA_FIELD_NUMBER: builtins.int TOPIC_FIELD_NUMBER: builtins.int + topic: builtins.str @property def data(self) -> global___OwnedBuffer: ... - topic: builtins.str def __init__( self, *, data: global___OwnedBuffer | None = ..., topic: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_topic", b"_topic", "data", b"data", "topic", b"topic"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_topic", b"_topic", "data", b"data", "topic", b"topic"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_topic", b"_topic"]) -> typing_extensions.Literal["topic"] | None: ... + def HasField(self, field_name: typing.Literal["data", b"data", "topic", b"topic"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data", b"data", "topic", b"topic"]) -> None: ... global___UserPacket = UserPacket -@typing_extensions.final +@typing.final +class ChatMessage(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + EDIT_TIMESTAMP_FIELD_NUMBER: builtins.int + DELETED_FIELD_NUMBER: builtins.int + GENERATED_FIELD_NUMBER: builtins.int + id: builtins.str + timestamp: builtins.int + message: builtins.str + edit_timestamp: builtins.int + deleted: builtins.bool + generated: builtins.bool + def __init__( + self, + *, + id: builtins.str | None = ..., + timestamp: builtins.int | None = ..., + message: builtins.str | None = ..., + edit_timestamp: builtins.int | None = ..., + deleted: builtins.bool | None = ..., + generated: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["deleted", b"deleted", "edit_timestamp", b"edit_timestamp", "generated", b"generated", "id", b"id", "message", b"message", "timestamp", b"timestamp"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["deleted", b"deleted", "edit_timestamp", b"edit_timestamp", "generated", b"generated", "id", b"id", "message", b"message", "timestamp", b"timestamp"]) -> None: ... + +global___ChatMessage = ChatMessage + +@typing.final +class ChatMessageReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MESSAGE_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def message(self) -> global___ChatMessage: ... + def __init__( + self, + *, + message: global___ChatMessage | None = ..., + participant_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["message", b"message", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["message", b"message", "participant_identity", b"participant_identity"]) -> None: ... + +global___ChatMessageReceived = ChatMessageReceived + +@typing.final class SipDTMF(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1430,29 +2379,25 @@ class SipDTMF(google.protobuf.message.Message): def __init__( self, *, - code: builtins.int = ..., + code: builtins.int | None = ..., digit: builtins.str | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_digit", b"_digit", "digit", b"digit"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_digit", b"_digit", "code", b"code", "digit", b"digit"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_digit", b"_digit"]) -> typing_extensions.Literal["digit"] | None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "digit", b"digit"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "digit", b"digit"]) -> None: ... global___SipDTMF = SipDTMF -@typing_extensions.final +@typing.final class DataPacketReceived(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor KIND_FIELD_NUMBER: builtins.int PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int - PARTICIPANT_SID_FIELD_NUMBER: builtins.int USER_FIELD_NUMBER: builtins.int SIP_DTMF_FIELD_NUMBER: builtins.int kind: global___DataPacketKind.ValueType participant_identity: builtins.str """Can be empty if the data is sent a server SDK""" - participant_sid: builtins.str - """Can be empty if the data is sent a server SDK""" @property def user(self) -> global___UserPacket: ... @property @@ -1460,22 +2405,41 @@ class DataPacketReceived(google.protobuf.message.Message): def __init__( self, *, - kind: global___DataPacketKind.ValueType = ..., - participant_identity: builtins.str = ..., - participant_sid: builtins.str | None = ..., + kind: global___DataPacketKind.ValueType | None = ..., + participant_identity: builtins.str | None = ..., user: global___UserPacket | None = ..., sip_dtmf: global___SipDTMF | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_participant_sid", b"_participant_sid", "participant_sid", b"participant_sid", "sip_dtmf", b"sip_dtmf", "user", b"user", "value", b"value"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_participant_sid", b"_participant_sid", "kind", b"kind", "participant_identity", b"participant_identity", "participant_sid", b"participant_sid", "sip_dtmf", b"sip_dtmf", "user", b"user", "value", b"value"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_participant_sid", b"_participant_sid"]) -> typing_extensions.Literal["participant_sid"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["value", b"value"]) -> typing_extensions.Literal["user", "sip_dtmf"] | None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "participant_identity", b"participant_identity", "sip_dtmf", b"sip_dtmf", "user", b"user", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "participant_identity", b"participant_identity", "sip_dtmf", b"sip_dtmf", "user", b"user", "value", b"value"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["value", b"value"]) -> typing.Literal["user", "sip_dtmf"] | None: ... global___DataPacketReceived = DataPacketReceived -@typing_extensions.final +@typing.final +class TranscriptionReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + TRACK_SID_FIELD_NUMBER: builtins.int + SEGMENTS_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + track_sid: builtins.str + @property + def segments(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___TranscriptionSegment]: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + track_sid: builtins.str | None = ..., + segments: collections.abc.Iterable[global___TranscriptionSegment] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "track_sid", b"track_sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "segments", b"segments", "track_sid", b"track_sid"]) -> None: ... + +global___TranscriptionReceived = TranscriptionReceived + +@typing.final class ConnectionStateChanged(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1484,13 +2448,14 @@ class ConnectionStateChanged(google.protobuf.message.Message): def __init__( self, *, - state: global___ConnectionState.ValueType = ..., + state: global___ConnectionState.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["state", b"state"]) -> None: ... + def HasField(self, field_name: typing.Literal["state", b"state"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["state", b"state"]) -> None: ... global___ConnectionStateChanged = ConnectionStateChanged -@typing_extensions.final +@typing.final class Connected(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1500,17 +2465,23 @@ class Connected(google.protobuf.message.Message): global___Connected = Connected -@typing_extensions.final +@typing.final class Disconnected(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor + REASON_FIELD_NUMBER: builtins.int + reason: participant_pb2.DisconnectReason.ValueType def __init__( self, + *, + reason: participant_pb2.DisconnectReason.ValueType | None = ..., ) -> None: ... + def HasField(self, field_name: typing.Literal["reason", b"reason"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["reason", b"reason"]) -> None: ... global___Disconnected = Disconnected -@typing_extensions.final +@typing.final class Reconnecting(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1520,7 +2491,7 @@ class Reconnecting(google.protobuf.message.Message): global___Reconnecting = Reconnecting -@typing_extensions.final +@typing.final class Reconnected(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1530,7 +2501,23 @@ class Reconnected(google.protobuf.message.Message): global___Reconnected = Reconnected -@typing_extensions.final +@typing.final +class TokenRefreshed(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TOKEN_FIELD_NUMBER: builtins.int + token: builtins.str + def __init__( + self, + *, + token: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["token", b"token"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["token", b"token"]) -> None: ... + +global___TokenRefreshed = TokenRefreshed + +@typing.final class RoomEOS(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1539,3 +2526,601 @@ class RoomEOS(google.protobuf.message.Message): ) -> None: ... global___RoomEOS = RoomEOS + +@typing.final +class DataStream(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + class _OperationType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + + class _OperationTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[DataStream._OperationType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CREATE: DataStream._OperationType.ValueType # 0 + UPDATE: DataStream._OperationType.ValueType # 1 + DELETE: DataStream._OperationType.ValueType # 2 + REACTION: DataStream._OperationType.ValueType # 3 + + class OperationType(_OperationType, metaclass=_OperationTypeEnumTypeWrapper): + """enum for operation types (specific to TextHeader)""" + + CREATE: DataStream.OperationType.ValueType # 0 + UPDATE: DataStream.OperationType.ValueType # 1 + DELETE: DataStream.OperationType.ValueType # 2 + REACTION: DataStream.OperationType.ValueType # 3 + + @typing.final + class TextHeader(google.protobuf.message.Message): + """header properties specific to text streams""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + OPERATION_TYPE_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + REPLY_TO_STREAM_ID_FIELD_NUMBER: builtins.int + ATTACHED_STREAM_IDS_FIELD_NUMBER: builtins.int + GENERATED_FIELD_NUMBER: builtins.int + operation_type: global___DataStream.OperationType.ValueType + version: builtins.int + """Optional: Version for updates/edits""" + reply_to_stream_id: builtins.str + """Optional: Reply to specific message""" + generated: builtins.bool + """true if the text has been generated by an agent from a participant's audio transcription""" + @property + def attached_stream_ids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """file attachments for text streams""" + + def __init__( + self, + *, + operation_type: global___DataStream.OperationType.ValueType | None = ..., + version: builtins.int | None = ..., + reply_to_stream_id: builtins.str | None = ..., + attached_stream_ids: collections.abc.Iterable[builtins.str] | None = ..., + generated: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["generated", b"generated", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "version", b"version"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attached_stream_ids", b"attached_stream_ids", "generated", b"generated", "operation_type", b"operation_type", "reply_to_stream_id", b"reply_to_stream_id", "version", b"version"]) -> None: ... + + @typing.final + class ByteHeader(google.protobuf.message.Message): + """header properties specific to byte or file streams""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + NAME_FIELD_NUMBER: builtins.int + name: builtins.str + def __init__( + self, + *, + name: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["name", b"name"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["name", b"name"]) -> None: ... + + @typing.final + class Header(google.protobuf.message.Message): + """main DataStream.Header that contains a oneof for specific headers""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + STREAM_ID_FIELD_NUMBER: builtins.int + TIMESTAMP_FIELD_NUMBER: builtins.int + MIME_TYPE_FIELD_NUMBER: builtins.int + TOPIC_FIELD_NUMBER: builtins.int + TOTAL_LENGTH_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + TEXT_HEADER_FIELD_NUMBER: builtins.int + BYTE_HEADER_FIELD_NUMBER: builtins.int + stream_id: builtins.str + """unique identifier for this data stream""" + timestamp: builtins.int + """using int64 for Unix timestamp""" + mime_type: builtins.str + topic: builtins.str + total_length: builtins.int + """only populated for finite streams, if it's a stream of unknown size this stays empty""" + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """user defined attributes map that can carry additional info""" + + @property + def text_header(self) -> global___DataStream.TextHeader: ... + @property + def byte_header(self) -> global___DataStream.ByteHeader: ... + def __init__( + self, + *, + stream_id: builtins.str | None = ..., + timestamp: builtins.int | None = ..., + mime_type: builtins.str | None = ..., + topic: builtins.str | None = ..., + total_length: builtins.int | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + text_header: global___DataStream.TextHeader | None = ..., + byte_header: global___DataStream.ByteHeader | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["byte_header", b"byte_header", "content_header", b"content_header", "mime_type", b"mime_type", "stream_id", b"stream_id", "text_header", b"text_header", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "byte_header", b"byte_header", "content_header", b"content_header", "mime_type", b"mime_type", "stream_id", b"stream_id", "text_header", b"text_header", "timestamp", b"timestamp", "topic", b"topic", "total_length", b"total_length"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["content_header", b"content_header"]) -> typing.Literal["text_header", "byte_header"] | None: ... + + @typing.final + class Chunk(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_ID_FIELD_NUMBER: builtins.int + CHUNK_INDEX_FIELD_NUMBER: builtins.int + CONTENT_FIELD_NUMBER: builtins.int + VERSION_FIELD_NUMBER: builtins.int + IV_FIELD_NUMBER: builtins.int + stream_id: builtins.str + """unique identifier for this data stream to map it to the correct header""" + chunk_index: builtins.int + content: builtins.bytes + """content as binary (bytes)""" + version: builtins.int + """a version indicating that this chunk_index has been retroactively modified and the original one needs to be replaced""" + iv: builtins.bytes + """optional, initialization vector for AES-GCM encryption""" + def __init__( + self, + *, + stream_id: builtins.str | None = ..., + chunk_index: builtins.int | None = ..., + content: builtins.bytes | None = ..., + version: builtins.int | None = ..., + iv: builtins.bytes | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chunk_index", b"chunk_index", "content", b"content", "iv", b"iv", "stream_id", b"stream_id", "version", b"version"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chunk_index", b"chunk_index", "content", b"content", "iv", b"iv", "stream_id", b"stream_id", "version", b"version"]) -> None: ... + + @typing.final + class Trailer(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + @typing.final + class AttributesEntry(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KEY_FIELD_NUMBER: builtins.int + VALUE_FIELD_NUMBER: builtins.int + key: builtins.str + value: builtins.str + def __init__( + self, + *, + key: builtins.str | None = ..., + value: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... + + STREAM_ID_FIELD_NUMBER: builtins.int + REASON_FIELD_NUMBER: builtins.int + ATTRIBUTES_FIELD_NUMBER: builtins.int + stream_id: builtins.str + """unique identifier for this data stream""" + reason: builtins.str + """reason why the stream was closed (could contain "error" / "interrupted" / empty for expected end)""" + @property + def attributes(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.str]: + """finalizing updates for the stream, can also include additional insights for errors or endTime for transcription""" + + def __init__( + self, + *, + stream_id: builtins.str | None = ..., + reason: builtins.str | None = ..., + attributes: collections.abc.Mapping[builtins.str, builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["reason", b"reason", "stream_id", b"stream_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["attributes", b"attributes", "reason", b"reason", "stream_id", b"stream_id"]) -> None: ... + + def __init__( + self, + ) -> None: ... + +global___DataStream = DataStream + +@typing.final +class DataStreamHeaderReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + HEADER_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def header(self) -> global___DataStream.Header: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + header: global___DataStream.Header | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["header", b"header", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["header", b"header", "participant_identity", b"participant_identity"]) -> None: ... + +global___DataStreamHeaderReceived = DataStreamHeaderReceived + +@typing.final +class DataStreamChunkReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + CHUNK_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def chunk(self) -> global___DataStream.Chunk: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + chunk: global___DataStream.Chunk | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chunk", b"chunk", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chunk", b"chunk", "participant_identity", b"participant_identity"]) -> None: ... + +global___DataStreamChunkReceived = DataStreamChunkReceived + +@typing.final +class DataStreamTrailerReceived(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + TRAILER_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def trailer(self) -> global___DataStream.Trailer: ... + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + trailer: global___DataStream.Trailer | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "trailer", b"trailer"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "trailer", b"trailer"]) -> None: ... + +global___DataStreamTrailerReceived = DataStreamTrailerReceived + +@typing.final +class SendStreamHeaderRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + HEADER_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + sender_identity: builtins.str + request_async_id: builtins.int + @property + def header(self) -> global___DataStream.Header: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + header: global___DataStream.Header | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + sender_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["header", b"header", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["destination_identities", b"destination_identities", "header", b"header", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> None: ... + +global___SendStreamHeaderRequest = SendStreamHeaderRequest + +@typing.final +class SendStreamChunkRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + CHUNK_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + sender_identity: builtins.str + request_async_id: builtins.int + @property + def chunk(self) -> global___DataStream.Chunk: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + chunk: global___DataStream.Chunk | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + sender_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["chunk", b"chunk", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["chunk", b"chunk", "destination_identities", b"destination_identities", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity"]) -> None: ... + +global___SendStreamChunkRequest = SendStreamChunkRequest + +@typing.final +class SendStreamTrailerRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + TRAILER_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITIES_FIELD_NUMBER: builtins.int + SENDER_IDENTITY_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + sender_identity: builtins.str + request_async_id: builtins.int + @property + def trailer(self) -> global___DataStream.Trailer: ... + @property + def destination_identities(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + trailer: global___DataStream.Trailer | None = ..., + destination_identities: collections.abc.Iterable[builtins.str] | None = ..., + sender_identity: builtins.str | None = ..., + request_async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity", "trailer", b"trailer"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["destination_identities", b"destination_identities", "local_participant_handle", b"local_participant_handle", "request_async_id", b"request_async_id", "sender_identity", b"sender_identity", "trailer", b"trailer"]) -> None: ... + +global___SendStreamTrailerRequest = SendStreamTrailerRequest + +@typing.final +class SendStreamHeaderResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___SendStreamHeaderResponse = SendStreamHeaderResponse + +@typing.final +class SendStreamChunkResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___SendStreamChunkResponse = SendStreamChunkResponse + +@typing.final +class SendStreamTrailerResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___SendStreamTrailerResponse = SendStreamTrailerResponse + +@typing.final +class SendStreamHeaderCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___SendStreamHeaderCallback = SendStreamHeaderCallback + +@typing.final +class SendStreamChunkCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___SendStreamChunkCallback = SendStreamChunkCallback + +@typing.final +class SendStreamTrailerCallback(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + error: builtins.str + def __init__( + self, + *, + async_id: builtins.int | None = ..., + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> None: ... + +global___SendStreamTrailerCallback = SendStreamTrailerCallback + +@typing.final +class SetDataChannelBufferedAmountLowThresholdRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + THRESHOLD_FIELD_NUMBER: builtins.int + KIND_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + threshold: builtins.int + kind: global___DataPacketKind.ValueType + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + threshold: builtins.int | None = ..., + kind: global___DataPacketKind.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "local_participant_handle", b"local_participant_handle", "threshold", b"threshold"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "local_participant_handle", b"local_participant_handle", "threshold", b"threshold"]) -> None: ... + +global___SetDataChannelBufferedAmountLowThresholdRequest = SetDataChannelBufferedAmountLowThresholdRequest + +@typing.final +class SetDataChannelBufferedAmountLowThresholdResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___SetDataChannelBufferedAmountLowThresholdResponse = SetDataChannelBufferedAmountLowThresholdResponse + +@typing.final +class DataChannelBufferedAmountLowThresholdChanged(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + KIND_FIELD_NUMBER: builtins.int + THRESHOLD_FIELD_NUMBER: builtins.int + kind: global___DataPacketKind.ValueType + threshold: builtins.int + def __init__( + self, + *, + kind: global___DataPacketKind.ValueType | None = ..., + threshold: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "threshold", b"threshold"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "threshold", b"threshold"]) -> None: ... + +global___DataChannelBufferedAmountLowThresholdChanged = DataChannelBufferedAmountLowThresholdChanged + +@typing.final +class ByteStreamOpened(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def reader(self) -> data_stream_pb2.OwnedByteStreamReader: ... + def __init__( + self, + *, + reader: data_stream_pb2.OwnedByteStreamReader | None = ..., + participant_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "reader", b"reader"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "reader", b"reader"]) -> None: ... + +global___ByteStreamOpened = ByteStreamOpened + +@typing.final +class TextStreamOpened(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + READER_FIELD_NUMBER: builtins.int + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + @property + def reader(self) -> data_stream_pb2.OwnedTextStreamReader: ... + def __init__( + self, + *, + reader: data_stream_pb2.OwnedTextStreamReader | None = ..., + participant_identity: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "reader", b"reader"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["participant_identity", b"participant_identity", "reader", b"reader"]) -> None: ... + +global___TextStreamOpened = TextStreamOpened + +@typing.final +class DataTrackPublished(google.protobuf.message.Message): + """A remote participant published a data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_FIELD_NUMBER: builtins.int + @property + def track(self) -> data_track_pb2.OwnedRemoteDataTrack: ... + def __init__( + self, + *, + track: data_track_pb2.OwnedRemoteDataTrack | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["track", b"track"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track", b"track"]) -> None: ... + +global___DataTrackPublished = DataTrackPublished + +@typing.final +class DataTrackUnpublished(google.protobuf.message.Message): + """A remote participant unpublished a data track.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + SID_FIELD_NUMBER: builtins.int + sid: builtins.str + """SID of the track that was unpublished.""" + def __init__( + self, + *, + sid: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["sid", b"sid"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["sid", b"sid"]) -> None: ... + +global___DataTrackUnpublished = DataTrackUnpublished diff --git a/livekit-rtc/livekit/rtc/_proto/rpc_pb2.py b/livekit-rtc/livekit/rtc/_proto/rpc_pb2.py new file mode 100644 index 00000000..a26363b1 --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/rpc_pb2.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: rpc.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\trpc.proto\x12\rlivekit.proto\"7\n\x08RpcError\x12\x0c\n\x04\x63ode\x18\x01 \x02(\r\x12\x0f\n\x07message\x18\x02 \x02(\t\x12\x0c\n\x04\x64\x61ta\x18\x03 \x01(\t\"\xce\x01\n\x11PerformRpcRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x1c\n\x14\x64\x65stination_identity\x18\x02 \x02(\t\x12\x0e\n\x06method\x18\x03 \x02(\t\x12\x0f\n\x07payload\x18\x04 \x02(\t\x12\x1b\n\x13response_timeout_ms\x18\x05 \x01(\r\x12\x18\n\x10request_async_id\x18\x06 \x01(\x04\x12!\n\x19max_round_trip_latency_ms\x18\x07 \x01(\r\"L\n\x18RegisterRpcMethodRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x0e\n\x06method\x18\x02 \x02(\t\"N\n\x1aUnregisterRpcMethodRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x0e\n\x06method\x18\x02 \x02(\t\"\x96\x01\n\"RpcMethodInvocationResponseRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x15\n\rinvocation_id\x18\x02 \x02(\x04\x12\x0f\n\x07payload\x18\x03 \x01(\t\x12&\n\x05\x65rror\x18\x04 \x01(\x0b\x32\x17.livekit.proto.RpcError\"&\n\x12PerformRpcResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"\x1b\n\x19RegisterRpcMethodResponse\"\x1d\n\x1bUnregisterRpcMethodResponse\"4\n#RpcMethodInvocationResponseResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\"_\n\x12PerformRpcCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\x0f\n\x07payload\x18\x02 \x01(\t\x12&\n\x05\x65rror\x18\x03 \x01(\x0b\x32\x17.livekit.proto.RpcError\"\xbe\x01\n\x18RpcMethodInvocationEvent\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12\x15\n\rinvocation_id\x18\x02 \x02(\x04\x12\x0e\n\x06method\x18\x03 \x02(\t\x12\x12\n\nrequest_id\x18\x04 \x02(\t\x12\x17\n\x0f\x63\x61ller_identity\x18\x05 \x02(\t\x12\x0f\n\x07payload\x18\x06 \x02(\t\x12\x1b\n\x13response_timeout_ms\x18\x07 \x02(\rB\x10\xaa\x02\rLiveKit.Proto') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'rpc_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' + _globals['_RPCERROR']._serialized_start=28 + _globals['_RPCERROR']._serialized_end=83 + _globals['_PERFORMRPCREQUEST']._serialized_start=86 + _globals['_PERFORMRPCREQUEST']._serialized_end=292 + _globals['_REGISTERRPCMETHODREQUEST']._serialized_start=294 + _globals['_REGISTERRPCMETHODREQUEST']._serialized_end=370 + _globals['_UNREGISTERRPCMETHODREQUEST']._serialized_start=372 + _globals['_UNREGISTERRPCMETHODREQUEST']._serialized_end=450 + _globals['_RPCMETHODINVOCATIONRESPONSEREQUEST']._serialized_start=453 + _globals['_RPCMETHODINVOCATIONRESPONSEREQUEST']._serialized_end=603 + _globals['_PERFORMRPCRESPONSE']._serialized_start=605 + _globals['_PERFORMRPCRESPONSE']._serialized_end=643 + _globals['_REGISTERRPCMETHODRESPONSE']._serialized_start=645 + _globals['_REGISTERRPCMETHODRESPONSE']._serialized_end=672 + _globals['_UNREGISTERRPCMETHODRESPONSE']._serialized_start=674 + _globals['_UNREGISTERRPCMETHODRESPONSE']._serialized_end=703 + _globals['_RPCMETHODINVOCATIONRESPONSERESPONSE']._serialized_start=705 + _globals['_RPCMETHODINVOCATIONRESPONSERESPONSE']._serialized_end=757 + _globals['_PERFORMRPCCALLBACK']._serialized_start=759 + _globals['_PERFORMRPCCALLBACK']._serialized_end=854 + _globals['_RPCMETHODINVOCATIONEVENT']._serialized_start=857 + _globals['_RPCMETHODINVOCATIONEVENT']._serialized_end=1047 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/rpc_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/rpc_pb2.pyi new file mode 100644 index 00000000..2d35cbae --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/rpc_pb2.pyi @@ -0,0 +1,261 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2025 LiveKit, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class RpcError(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CODE_FIELD_NUMBER: builtins.int + MESSAGE_FIELD_NUMBER: builtins.int + DATA_FIELD_NUMBER: builtins.int + code: builtins.int + message: builtins.str + data: builtins.str + def __init__( + self, + *, + code: builtins.int | None = ..., + message: builtins.str | None = ..., + data: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["code", b"code", "data", b"data", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["code", b"code", "data", b"data", "message", b"message"]) -> None: ... + +global___RpcError = RpcError + +@typing.final +class PerformRpcRequest(google.protobuf.message.Message): + """FFI Requests""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + DESTINATION_IDENTITY_FIELD_NUMBER: builtins.int + METHOD_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int + RESPONSE_TIMEOUT_MS_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int + MAX_ROUND_TRIP_LATENCY_MS_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + destination_identity: builtins.str + method: builtins.str + payload: builtins.str + response_timeout_ms: builtins.int + request_async_id: builtins.int + max_round_trip_latency_ms: builtins.int + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + destination_identity: builtins.str | None = ..., + method: builtins.str | None = ..., + payload: builtins.str | None = ..., + response_timeout_ms: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., + max_round_trip_latency_ms: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["destination_identity", b"destination_identity", "local_participant_handle", b"local_participant_handle", "max_round_trip_latency_ms", b"max_round_trip_latency_ms", "method", b"method", "payload", b"payload", "request_async_id", b"request_async_id", "response_timeout_ms", b"response_timeout_ms"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["destination_identity", b"destination_identity", "local_participant_handle", b"local_participant_handle", "max_round_trip_latency_ms", b"max_round_trip_latency_ms", "method", b"method", "payload", b"payload", "request_async_id", b"request_async_id", "response_timeout_ms", b"response_timeout_ms"]) -> None: ... + +global___PerformRpcRequest = PerformRpcRequest + +@typing.final +class RegisterRpcMethodRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + METHOD_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + method: builtins.str + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + method: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "method", b"method"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "method", b"method"]) -> None: ... + +global___RegisterRpcMethodRequest = RegisterRpcMethodRequest + +@typing.final +class UnregisterRpcMethodRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + METHOD_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + method: builtins.str + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + method: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "method", b"method"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_participant_handle", b"local_participant_handle", "method", b"method"]) -> None: ... + +global___UnregisterRpcMethodRequest = UnregisterRpcMethodRequest + +@typing.final +class RpcMethodInvocationResponseRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + INVOCATION_ID_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + invocation_id: builtins.int + payload: builtins.str + @property + def error(self) -> global___RpcError: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + invocation_id: builtins.int | None = ..., + payload: builtins.str | None = ..., + error: global___RpcError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error", "invocation_id", b"invocation_id", "local_participant_handle", b"local_participant_handle", "payload", b"payload"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error", "invocation_id", b"invocation_id", "local_participant_handle", b"local_participant_handle", "payload", b"payload"]) -> None: ... + +global___RpcMethodInvocationResponseRequest = RpcMethodInvocationResponseRequest + +@typing.final +class PerformRpcResponse(google.protobuf.message.Message): + """FFI Responses""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + async_id: builtins.int + def __init__( + self, + *, + async_id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... + +global___PerformRpcResponse = PerformRpcResponse + +@typing.final +class RegisterRpcMethodResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___RegisterRpcMethodResponse = RegisterRpcMethodResponse + +@typing.final +class UnregisterRpcMethodResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___UnregisterRpcMethodResponse = UnregisterRpcMethodResponse + +@typing.final +class RpcMethodInvocationResponseResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ERROR_FIELD_NUMBER: builtins.int + error: builtins.str + def __init__( + self, + *, + error: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["error", b"error"]) -> None: ... + +global___RpcMethodInvocationResponseResponse = RpcMethodInvocationResponseResponse + +@typing.final +class PerformRpcCallback(google.protobuf.message.Message): + """FFI Callbacks""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ASYNC_ID_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int + ERROR_FIELD_NUMBER: builtins.int + async_id: builtins.int + payload: builtins.str + @property + def error(self) -> global___RpcError: ... + def __init__( + self, + *, + async_id: builtins.int | None = ..., + payload: builtins.str | None = ..., + error: global___RpcError | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "payload", b"payload"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "payload", b"payload"]) -> None: ... + +global___PerformRpcCallback = PerformRpcCallback + +@typing.final +class RpcMethodInvocationEvent(google.protobuf.message.Message): + """FFI Events""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + INVOCATION_ID_FIELD_NUMBER: builtins.int + METHOD_FIELD_NUMBER: builtins.int + REQUEST_ID_FIELD_NUMBER: builtins.int + CALLER_IDENTITY_FIELD_NUMBER: builtins.int + PAYLOAD_FIELD_NUMBER: builtins.int + RESPONSE_TIMEOUT_MS_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + invocation_id: builtins.int + method: builtins.str + request_id: builtins.str + caller_identity: builtins.str + payload: builtins.str + response_timeout_ms: builtins.int + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + invocation_id: builtins.int | None = ..., + method: builtins.str | None = ..., + request_id: builtins.str | None = ..., + caller_identity: builtins.str | None = ..., + payload: builtins.str | None = ..., + response_timeout_ms: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["caller_identity", b"caller_identity", "invocation_id", b"invocation_id", "local_participant_handle", b"local_participant_handle", "method", b"method", "payload", b"payload", "request_id", b"request_id", "response_timeout_ms", b"response_timeout_ms"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["caller_identity", b"caller_identity", "invocation_id", b"invocation_id", "local_participant_handle", b"local_participant_handle", "method", b"method", "payload", b"payload", "request_id", b"request_id", "response_timeout_ms", b"response_timeout_ms"]) -> None: ... + +global___RpcMethodInvocationEvent = RpcMethodInvocationEvent diff --git a/livekit-rtc/livekit/rtc/_proto/stats_pb2.py b/livekit-rtc/livekit/rtc/_proto/stats_pb2.py index 8c68ca21..ce7b411c 100644 --- a/livekit-rtc/livekit/rtc/_proto/stats_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/stats_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: stats.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -14,7 +14,7 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bstats.proto\x12\rlivekit.proto\"\xd9\x17\n\x08RtcStats\x12.\n\x05\x63odec\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.RtcStats.CodecH\x00\x12\x39\n\x0binbound_rtp\x18\x04 \x01(\x0b\x32\".livekit.proto.RtcStats.InboundRtpH\x00\x12;\n\x0coutbound_rtp\x18\x05 \x01(\x0b\x32#.livekit.proto.RtcStats.OutboundRtpH\x00\x12\x46\n\x12remote_inbound_rtp\x18\x06 \x01(\x0b\x32(.livekit.proto.RtcStats.RemoteInboundRtpH\x00\x12H\n\x13remote_outbound_rtp\x18\x07 \x01(\x0b\x32).livekit.proto.RtcStats.RemoteOutboundRtpH\x00\x12;\n\x0cmedia_source\x18\x08 \x01(\x0b\x32#.livekit.proto.RtcStats.MediaSourceH\x00\x12=\n\rmedia_playout\x18\t \x01(\x0b\x32$.livekit.proto.RtcStats.MediaPlayoutH\x00\x12\x41\n\x0fpeer_connection\x18\n \x01(\x0b\x32&.livekit.proto.RtcStats.PeerConnectionH\x00\x12;\n\x0c\x64\x61ta_channel\x18\x0b \x01(\x0b\x32#.livekit.proto.RtcStats.DataChannelH\x00\x12\x36\n\ttransport\x18\x0c \x01(\x0b\x32!.livekit.proto.RtcStats.TransportH\x00\x12?\n\x0e\x63\x61ndidate_pair\x18\r \x01(\x0b\x32%.livekit.proto.RtcStats.CandidatePairH\x00\x12\x41\n\x0flocal_candidate\x18\x0e \x01(\x0b\x32&.livekit.proto.RtcStats.LocalCandidateH\x00\x12\x43\n\x10remote_candidate\x18\x0f \x01(\x0b\x32\'.livekit.proto.RtcStats.RemoteCandidateH\x00\x12:\n\x0b\x63\x65rtificate\x18\x10 \x01(\x0b\x32#.livekit.proto.RtcStats.CertificateH\x00\x12.\n\x05track\x18\x11 \x01(\x0b\x32\x1d.livekit.proto.RtcStats.TrackH\x00\x1a[\n\x05\x43odec\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12(\n\x05\x63odec\x18\x02 \x01(\x0b\x32\x19.livekit.proto.CodecStats\x1a\xd5\x01\n\nInboundRtp\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12\x37\n\x08received\x18\x03 \x01(\x0b\x32%.livekit.proto.ReceivedRtpStreamStats\x12\x35\n\x07inbound\x18\x04 \x01(\x0b\x32$.livekit.proto.InboundRtpStreamStats\x1a\xd0\x01\n\x0bOutboundRtp\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12/\n\x04sent\x18\x03 \x01(\x0b\x32!.livekit.proto.SentRtpStreamStats\x12\x37\n\x08outbound\x18\x04 \x01(\x0b\x32%.livekit.proto.OutboundRtpStreamStats\x1a\xe8\x01\n\x10RemoteInboundRtp\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12\x37\n\x08received\x18\x03 \x01(\x0b\x32%.livekit.proto.ReceivedRtpStreamStats\x12\x42\n\x0eremote_inbound\x18\x04 \x01(\x0b\x32*.livekit.proto.RemoteInboundRtpStreamStats\x1a\xe3\x01\n\x11RemoteOutboundRtp\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12/\n\x04sent\x18\x03 \x01(\x0b\x32!.livekit.proto.SentRtpStreamStats\x12\x44\n\x0fremote_outbound\x18\x04 \x01(\x0b\x32+.livekit.proto.RemoteOutboundRtpStreamStats\x1a\xc8\x01\n\x0bMediaSource\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12/\n\x06source\x18\x02 \x01(\x0b\x32\x1f.livekit.proto.MediaSourceStats\x12.\n\x05\x61udio\x18\x03 \x01(\x0b\x32\x1f.livekit.proto.AudioSourceStats\x12.\n\x05video\x18\x04 \x01(\x0b\x32\x1f.livekit.proto.VideoSourceStats\x1aq\n\x0cMediaPlayout\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x37\n\raudio_playout\x18\x02 \x01(\x0b\x32 .livekit.proto.AudioPlayoutStats\x1aj\n\x0ePeerConnection\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12.\n\x02pc\x18\x02 \x01(\x0b\x32\".livekit.proto.PeerConnectionStats\x1a\x64\n\x0b\x44\x61taChannel\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12+\n\x02\x64\x63\x18\x02 \x01(\x0b\x32\x1f.livekit.proto.DataChannelStats\x1ag\n\tTransport\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x30\n\ttransport\x18\x02 \x01(\x0b\x32\x1d.livekit.proto.TransportStats\x1at\n\rCandidatePair\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x39\n\x0e\x63\x61ndidate_pair\x18\x02 \x01(\x0b\x32!.livekit.proto.CandidatePairStats\x1ao\n\x0eLocalCandidate\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x33\n\tcandidate\x18\x02 \x01(\x0b\x32 .livekit.proto.IceCandidateStats\x1ap\n\x0fRemoteCandidate\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x33\n\tcandidate\x18\x02 \x01(\x0b\x32 .livekit.proto.IceCandidateStats\x1am\n\x0b\x43\x65rtificate\x12(\n\x03rtc\x18\x01 \x01(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x34\n\x0b\x63\x65rtificate\x18\x02 \x01(\x0b\x32\x1f.livekit.proto.CertificateStats\x1a\x07\n\x05TrackB\x07\n\x05stats\"-\n\x0cRtcStatsData\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\"\x88\x01\n\nCodecStats\x12\x14\n\x0cpayload_type\x18\x01 \x01(\r\x12\x14\n\x0ctransport_id\x18\x02 \x01(\t\x12\x11\n\tmime_type\x18\x03 \x01(\t\x12\x12\n\nclock_rate\x18\x04 \x01(\r\x12\x10\n\x08\x63hannels\x18\x05 \x01(\r\x12\x15\n\rsdp_fmtp_line\x18\x06 \x01(\t\"T\n\x0eRtpStreamStats\x12\x0c\n\x04ssrc\x18\x01 \x01(\r\x12\x0c\n\x04kind\x18\x02 \x01(\t\x12\x14\n\x0ctransport_id\x18\x03 \x01(\t\x12\x10\n\x08\x63odec_id\x18\x04 \x01(\t\"X\n\x16ReceivedRtpStreamStats\x12\x18\n\x10packets_received\x18\x01 \x01(\x04\x12\x14\n\x0cpackets_lost\x18\x02 \x01(\x03\x12\x0e\n\x06jitter\x18\x03 \x01(\x01\"\x82\x0c\n\x15InboundRtpStreamStats\x12\x18\n\x10track_identifier\x18\x01 \x01(\t\x12\x0b\n\x03mid\x18\x02 \x01(\t\x12\x11\n\tremote_id\x18\x03 \x01(\t\x12\x16\n\x0e\x66rames_decoded\x18\x04 \x01(\r\x12\x1a\n\x12key_frames_decoded\x18\x05 \x01(\r\x12\x17\n\x0f\x66rames_rendered\x18\x06 \x01(\r\x12\x16\n\x0e\x66rames_dropped\x18\x07 \x01(\r\x12\x13\n\x0b\x66rame_width\x18\x08 \x01(\r\x12\x14\n\x0c\x66rame_height\x18\t \x01(\r\x12\x19\n\x11\x66rames_per_second\x18\n \x01(\x01\x12\x0e\n\x06qp_sum\x18\x0b \x01(\x04\x12\x19\n\x11total_decode_time\x18\x0c \x01(\x01\x12\x1f\n\x17total_inter_frame_delay\x18\r \x01(\x01\x12\'\n\x1ftotal_squared_inter_frame_delay\x18\x0e \x01(\x01\x12\x13\n\x0bpause_count\x18\x0f \x01(\r\x12\x1c\n\x14total_pause_duration\x18\x10 \x01(\x01\x12\x14\n\x0c\x66reeze_count\x18\x11 \x01(\r\x12\x1d\n\x15total_freeze_duration\x18\x12 \x01(\x01\x12&\n\x1elast_packet_received_timestamp\x18\x13 \x01(\x01\x12\x1d\n\x15header_bytes_received\x18\x14 \x01(\x04\x12\x19\n\x11packets_discarded\x18\x15 \x01(\x04\x12\x1a\n\x12\x66\x65\x63_bytes_received\x18\x16 \x01(\x04\x12\x1c\n\x14\x66\x65\x63_packets_received\x18\x17 \x01(\x04\x12\x1d\n\x15\x66\x65\x63_packets_discarded\x18\x18 \x01(\x04\x12\x16\n\x0e\x62ytes_received\x18\x19 \x01(\x04\x12\x12\n\nnack_count\x18\x1a \x01(\r\x12\x11\n\tfir_count\x18\x1b \x01(\r\x12\x11\n\tpli_count\x18\x1c \x01(\r\x12\x1e\n\x16total_processing_delay\x18\x1d \x01(\x01\x12#\n\x1b\x65stimated_playout_timestamp\x18\x1e \x01(\x01\x12\x1b\n\x13jitter_buffer_delay\x18\x1f \x01(\x01\x12\"\n\x1ajitter_buffer_target_delay\x18 \x01(\x01\x12#\n\x1bjitter_buffer_emitted_count\x18! \x01(\x04\x12#\n\x1bjitter_buffer_minimum_delay\x18\" \x01(\x01\x12\x1e\n\x16total_samples_received\x18# \x01(\x04\x12\x19\n\x11\x63oncealed_samples\x18$ \x01(\x04\x12 \n\x18silent_concealed_samples\x18% \x01(\x04\x12\x1a\n\x12\x63oncealment_events\x18& \x01(\x04\x12)\n!inserted_samples_for_deceleration\x18\' \x01(\x04\x12(\n removed_samples_for_acceleration\x18( \x01(\x04\x12\x13\n\x0b\x61udio_level\x18) \x01(\x01\x12\x1a\n\x12total_audio_energy\x18* \x01(\x01\x12\x1e\n\x16total_samples_duration\x18+ \x01(\x01\x12\x17\n\x0f\x66rames_received\x18, \x01(\x04\x12\x1e\n\x16\x64\x65\x63oder_implementation\x18- \x01(\t\x12\x12\n\nplayout_id\x18. \x01(\t\x12\x1f\n\x17power_efficient_decoder\x18/ \x01(\x08\x12.\n&frames_assembled_from_multiple_packets\x18\x30 \x01(\x04\x12\x1b\n\x13total_assembly_time\x18\x31 \x01(\x01\x12&\n\x1eretransmitted_packets_received\x18\x32 \x01(\x04\x12$\n\x1cretransmitted_bytes_received\x18\x33 \x01(\x04\x12\x10\n\x08rtx_ssrc\x18\x34 \x01(\r\x12\x10\n\x08\x66\x65\x63_ssrc\x18\x35 \x01(\r\">\n\x12SentRtpStreamStats\x12\x14\n\x0cpackets_sent\x18\x01 \x01(\x04\x12\x12\n\nbytes_sent\x18\x02 \x01(\x04\"\xd1\x07\n\x16OutboundRtpStreamStats\x12\x0b\n\x03mid\x18\x01 \x01(\t\x12\x17\n\x0fmedia_source_id\x18\x02 \x01(\t\x12\x11\n\tremote_id\x18\x03 \x01(\t\x12\x0b\n\x03rid\x18\x04 \x01(\t\x12\x19\n\x11header_bytes_sent\x18\x05 \x01(\x04\x12\"\n\x1aretransmitted_packets_sent\x18\x06 \x01(\x04\x12 \n\x18retransmitted_bytes_sent\x18\x07 \x01(\x04\x12\x10\n\x08rtx_ssrc\x18\x08 \x01(\r\x12\x16\n\x0etarget_bitrate\x18\t \x01(\x01\x12\"\n\x1atotal_encoded_bytes_target\x18\n \x01(\x04\x12\x13\n\x0b\x66rame_width\x18\x0b \x01(\r\x12\x14\n\x0c\x66rame_height\x18\x0c \x01(\r\x12\x19\n\x11\x66rames_per_second\x18\r \x01(\x01\x12\x13\n\x0b\x66rames_sent\x18\x0e \x01(\r\x12\x18\n\x10huge_frames_sent\x18\x0f \x01(\r\x12\x16\n\x0e\x66rames_encoded\x18\x10 \x01(\r\x12\x1a\n\x12key_frames_encoded\x18\x11 \x01(\r\x12\x0e\n\x06qp_sum\x18\x12 \x01(\x04\x12\x19\n\x11total_encode_time\x18\x13 \x01(\x01\x12\x1f\n\x17total_packet_send_delay\x18\x14 \x01(\x01\x12I\n\x19quality_limitation_reason\x18\x15 \x01(\x0e\x32&.livekit.proto.QualityLimitationReason\x12k\n\x1cquality_limitation_durations\x18\x16 \x03(\x0b\x32\x45.livekit.proto.OutboundRtpStreamStats.QualityLimitationDurationsEntry\x12-\n%quality_limitation_resolution_changes\x18\x17 \x01(\r\x12\x12\n\nnack_count\x18\x18 \x01(\r\x12\x11\n\tfir_count\x18\x19 \x01(\r\x12\x11\n\tpli_count\x18\x1a \x01(\r\x12\x1e\n\x16\x65ncoder_implementation\x18\x1b \x01(\t\x12\x1f\n\x17power_efficient_encoder\x18\x1c \x01(\x08\x12\x0e\n\x06\x61\x63tive\x18\x1d \x01(\x08\x12\x18\n\x10scalibility_mode\x18\x1e \x01(\t\x1a\x41\n\x1fQualityLimitationDurationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x01:\x02\x38\x01\"\xa4\x01\n\x1bRemoteInboundRtpStreamStats\x12\x10\n\x08local_id\x18\x01 \x01(\t\x12\x17\n\x0fround_trip_time\x18\x02 \x01(\x01\x12\x1d\n\x15total_round_trip_time\x18\x03 \x01(\x01\x12\x15\n\rfraction_lost\x18\x04 \x01(\x01\x12$\n\x1cround_trip_time_measurements\x18\x05 \x01(\x04\"\xbe\x01\n\x1cRemoteOutboundRtpStreamStats\x12\x10\n\x08local_id\x18\x01 \x01(\t\x12\x18\n\x10remote_timestamp\x18\x02 \x01(\x01\x12\x14\n\x0creports_sent\x18\x03 \x01(\x04\x12\x17\n\x0fround_trip_time\x18\x04 \x01(\x01\x12\x1d\n\x15total_round_trip_time\x18\x05 \x01(\x01\x12$\n\x1cround_trip_time_measurements\x18\x06 \x01(\x04\":\n\x10MediaSourceStats\x12\x18\n\x10track_identifier\x18\x01 \x01(\t\x12\x0c\n\x04kind\x18\x02 \x01(\t\"\xa2\x02\n\x10\x41udioSourceStats\x12\x13\n\x0b\x61udio_level\x18\x01 \x01(\x01\x12\x1a\n\x12total_audio_energy\x18\x02 \x01(\x01\x12\x1e\n\x16total_samples_duration\x18\x03 \x01(\x01\x12\x18\n\x10\x65\x63ho_return_loss\x18\x04 \x01(\x01\x12$\n\x1c\x65\x63ho_return_loss_enhancement\x18\x05 \x01(\x01\x12 \n\x18\x64ropped_samples_duration\x18\x06 \x01(\x01\x12\x1e\n\x16\x64ropped_samples_events\x18\x07 \x01(\r\x12\x1b\n\x13total_capture_delay\x18\x08 \x01(\x01\x12\x1e\n\x16total_samples_captured\x18\t \x01(\x04\"\\\n\x10VideoSourceStats\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x0e\n\x06\x66rames\x18\x03 \x01(\r\x12\x19\n\x11\x66rames_per_second\x18\x04 \x01(\x01\"\xc5\x01\n\x11\x41udioPlayoutStats\x12\x0c\n\x04kind\x18\x01 \x01(\t\x12$\n\x1csynthesized_samples_duration\x18\x02 \x01(\x01\x12\"\n\x1asynthesized_samples_events\x18\x03 \x01(\r\x12\x1e\n\x16total_samples_duration\x18\x04 \x01(\x01\x12\x1b\n\x13total_playout_delay\x18\x05 \x01(\x01\x12\x1b\n\x13total_samples_count\x18\x06 \x01(\x04\"Q\n\x13PeerConnectionStats\x12\x1c\n\x14\x64\x61ta_channels_opened\x18\x01 \x01(\r\x12\x1c\n\x14\x64\x61ta_channels_closed\x18\x02 \x01(\r\"\xf1\x01\n\x10\x44\x61taChannelStats\x12\r\n\x05label\x18\x01 \x01(\t\x12\x10\n\x08protocol\x18\x02 \x01(\t\x12\x1f\n\x17\x64\x61ta_channel_identifier\x18\x03 \x01(\x05\x12\x33\n\x05state\x18\x04 \x01(\x0e\x32\x1f.livekit.proto.DataChannelStateH\x00\x88\x01\x01\x12\x15\n\rmessages_sent\x18\x05 \x01(\r\x12\x12\n\nbytes_sent\x18\x06 \x01(\x04\x12\x19\n\x11messages_received\x18\x07 \x01(\r\x12\x16\n\x0e\x62ytes_received\x18\x08 \x01(\x04\x42\x08\n\x06_state\"\xc3\x04\n\x0eTransportStats\x12\x14\n\x0cpackets_sent\x18\x01 \x01(\x04\x12\x18\n\x10packets_received\x18\x02 \x01(\x04\x12\x12\n\nbytes_sent\x18\x03 \x01(\x04\x12\x16\n\x0e\x62ytes_received\x18\x04 \x01(\x04\x12(\n\x08ice_role\x18\x05 \x01(\x0e\x32\x16.livekit.proto.IceRole\x12#\n\x1bice_local_username_fragment\x18\x06 \x01(\t\x12:\n\ndtls_state\x18\x07 \x01(\x0e\x32!.livekit.proto.DtlsTransportStateH\x00\x88\x01\x01\x12\x38\n\tice_state\x18\x08 \x01(\x0e\x32 .livekit.proto.IceTransportStateH\x01\x88\x01\x01\x12\"\n\x1aselected_candidate_pair_id\x18\t \x01(\t\x12\x1c\n\x14local_certificate_id\x18\n \x01(\t\x12\x1d\n\x15remote_certificate_id\x18\x0b \x01(\t\x12\x13\n\x0btls_version\x18\x0c \x01(\t\x12\x13\n\x0b\x64tls_cipher\x18\r \x01(\t\x12*\n\tdtls_role\x18\x0e \x01(\x0e\x32\x17.livekit.proto.DtlsRole\x12\x13\n\x0bsrtp_cipher\x18\x0f \x01(\t\x12\'\n\x1fselected_candidate_pair_changes\x18\x10 \x01(\rB\r\n\x0b_dtls_stateB\x0c\n\n_ice_state\"\xb3\x05\n\x12\x43\x61ndidatePairStats\x12\x14\n\x0ctransport_id\x18\x01 \x01(\t\x12\x1a\n\x12local_candidate_id\x18\x02 \x01(\t\x12\x1b\n\x13remote_candidate_id\x18\x03 \x01(\t\x12\x38\n\x05state\x18\x04 \x01(\x0e\x32$.livekit.proto.IceCandidatePairStateH\x00\x88\x01\x01\x12\x11\n\tnominated\x18\x05 \x01(\x08\x12\x14\n\x0cpackets_sent\x18\x06 \x01(\x04\x12\x18\n\x10packets_received\x18\x07 \x01(\x04\x12\x12\n\nbytes_sent\x18\x08 \x01(\x04\x12\x16\n\x0e\x62ytes_received\x18\t \x01(\x04\x12\"\n\x1alast_packet_sent_timestamp\x18\n \x01(\x01\x12&\n\x1elast_packet_received_timestamp\x18\x0b \x01(\x01\x12\x1d\n\x15total_round_trip_time\x18\x0c \x01(\x01\x12\x1f\n\x17\x63urrent_round_trip_time\x18\r \x01(\x01\x12\"\n\x1a\x61vailable_outgoing_bitrate\x18\x0e \x01(\x01\x12\"\n\x1a\x61vailable_incoming_bitrate\x18\x0f \x01(\x01\x12\x19\n\x11requests_received\x18\x10 \x01(\x04\x12\x15\n\rrequests_sent\x18\x11 \x01(\x04\x12\x1a\n\x12responses_received\x18\x12 \x01(\x04\x12\x16\n\x0eresponses_sent\x18\x13 \x01(\x04\x12\x1d\n\x15\x63onsent_requests_sent\x18\x14 \x01(\x04\x12!\n\x19packets_discarded_on_send\x18\x15 \x01(\r\x12\x1f\n\x17\x62ytes_discarded_on_send\x18\x16 \x01(\x04\x42\x08\n\x06_state\"\xcb\x03\n\x11IceCandidateStats\x12\x14\n\x0ctransport_id\x18\x01 \x01(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x01(\t\x12\x0c\n\x04port\x18\x03 \x01(\x05\x12\x10\n\x08protocol\x18\x04 \x01(\t\x12<\n\x0e\x63\x61ndidate_type\x18\x05 \x01(\x0e\x32\x1f.livekit.proto.IceCandidateTypeH\x00\x88\x01\x01\x12\x10\n\x08priority\x18\x06 \x01(\x05\x12\x0b\n\x03url\x18\x07 \x01(\t\x12\x46\n\x0erelay_protocol\x18\x08 \x01(\x0e\x32).livekit.proto.IceServerTransportProtocolH\x01\x88\x01\x01\x12\x12\n\nfoundation\x18\t \x01(\t\x12\x17\n\x0frelated_address\x18\n \x01(\t\x12\x14\n\x0crelated_port\x18\x0b \x01(\x05\x12\x19\n\x11username_fragment\x18\x0c \x01(\t\x12\x39\n\x08tcp_type\x18\r \x01(\x0e\x32\".livekit.proto.IceTcpCandidateTypeH\x02\x88\x01\x01\x42\x11\n\x0f_candidate_typeB\x11\n\x0f_relay_protocolB\x0b\n\t_tcp_type\"\x81\x01\n\x10\x43\x65rtificateStats\x12\x13\n\x0b\x66ingerprint\x18\x01 \x01(\t\x12\x1d\n\x15\x66ingerprint_algorithm\x18\x02 \x01(\t\x12\x1a\n\x12\x62\x61se64_certificate\x18\x03 \x01(\t\x12\x1d\n\x15issuer_certificate_id\x18\x04 \x01(\t*Q\n\x10\x44\x61taChannelState\x12\x11\n\rDC_CONNECTING\x10\x00\x12\x0b\n\x07\x44\x43_OPEN\x10\x01\x12\x0e\n\nDC_CLOSING\x10\x02\x12\r\n\tDC_CLOSED\x10\x03*r\n\x17QualityLimitationReason\x12\x13\n\x0fLIMITATION_NONE\x10\x00\x12\x12\n\x0eLIMITATION_CPU\x10\x01\x12\x18\n\x14LIMITATION_BANDWIDTH\x10\x02\x12\x14\n\x10LIMITATION_OTHER\x10\x03*C\n\x07IceRole\x12\x0f\n\x0bICE_UNKNOWN\x10\x00\x12\x13\n\x0fICE_CONTROLLING\x10\x01\x12\x12\n\x0eICE_CONTROLLED\x10\x02*\x9f\x01\n\x12\x44tlsTransportState\x12\x16\n\x12\x44TLS_TRANSPORT_NEW\x10\x00\x12\x1d\n\x19\x44TLS_TRANSPORT_CONNECTING\x10\x01\x12\x1c\n\x18\x44TLS_TRANSPORT_CONNECTED\x10\x02\x12\x19\n\x15\x44TLS_TRANSPORT_CLOSED\x10\x03\x12\x19\n\x15\x44TLS_TRANSPORT_FAILED\x10\x04*\xd4\x01\n\x11IceTransportState\x12\x15\n\x11ICE_TRANSPORT_NEW\x10\x00\x12\x1a\n\x16ICE_TRANSPORT_CHECKING\x10\x01\x12\x1b\n\x17ICE_TRANSPORT_CONNECTED\x10\x02\x12\x1b\n\x17ICE_TRANSPORT_COMPLETED\x10\x03\x12\x1e\n\x1aICE_TRANSPORT_DISCONNECTED\x10\x04\x12\x18\n\x14ICE_TRANSPORT_FAILED\x10\x05\x12\x18\n\x14ICE_TRANSPORT_CLOSED\x10\x06*>\n\x08\x44tlsRole\x12\x0f\n\x0b\x44TLS_CLIENT\x10\x00\x12\x0f\n\x0b\x44TLS_SERVER\x10\x01\x12\x10\n\x0c\x44TLS_UNKNOWN\x10\x02*u\n\x15IceCandidatePairState\x12\x0f\n\x0bPAIR_FROZEN\x10\x00\x12\x10\n\x0cPAIR_WAITING\x10\x01\x12\x14\n\x10PAIR_IN_PROGRESS\x10\x02\x12\x0f\n\x0bPAIR_FAILED\x10\x03\x12\x12\n\x0ePAIR_SUCCEEDED\x10\x04*=\n\x10IceCandidateType\x12\x08\n\x04HOST\x10\x00\x12\t\n\x05SRFLX\x10\x01\x12\t\n\x05PRFLX\x10\x02\x12\t\n\x05RELAY\x10\x03*U\n\x1aIceServerTransportProtocol\x12\x11\n\rTRANSPORT_UDP\x10\x00\x12\x11\n\rTRANSPORT_TCP\x10\x01\x12\x11\n\rTRANSPORT_TLS\x10\x02*T\n\x13IceTcpCandidateType\x12\x14\n\x10\x43\x41NDIDATE_ACTIVE\x10\x00\x12\x15\n\x11\x43\x41NDIDATE_PASSIVE\x10\x01\x12\x10\n\x0c\x43\x41NDIDATE_SO\x10\x02\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0bstats.proto\x12\rlivekit.proto\"\xeb\x18\n\x08RtcStats\x12.\n\x05\x63odec\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.RtcStats.CodecH\x00\x12\x39\n\x0binbound_rtp\x18\x04 \x01(\x0b\x32\".livekit.proto.RtcStats.InboundRtpH\x00\x12;\n\x0coutbound_rtp\x18\x05 \x01(\x0b\x32#.livekit.proto.RtcStats.OutboundRtpH\x00\x12\x46\n\x12remote_inbound_rtp\x18\x06 \x01(\x0b\x32(.livekit.proto.RtcStats.RemoteInboundRtpH\x00\x12H\n\x13remote_outbound_rtp\x18\x07 \x01(\x0b\x32).livekit.proto.RtcStats.RemoteOutboundRtpH\x00\x12;\n\x0cmedia_source\x18\x08 \x01(\x0b\x32#.livekit.proto.RtcStats.MediaSourceH\x00\x12=\n\rmedia_playout\x18\t \x01(\x0b\x32$.livekit.proto.RtcStats.MediaPlayoutH\x00\x12\x41\n\x0fpeer_connection\x18\n \x01(\x0b\x32&.livekit.proto.RtcStats.PeerConnectionH\x00\x12;\n\x0c\x64\x61ta_channel\x18\x0b \x01(\x0b\x32#.livekit.proto.RtcStats.DataChannelH\x00\x12\x36\n\ttransport\x18\x0c \x01(\x0b\x32!.livekit.proto.RtcStats.TransportH\x00\x12?\n\x0e\x63\x61ndidate_pair\x18\r \x01(\x0b\x32%.livekit.proto.RtcStats.CandidatePairH\x00\x12\x41\n\x0flocal_candidate\x18\x0e \x01(\x0b\x32&.livekit.proto.RtcStats.LocalCandidateH\x00\x12\x43\n\x10remote_candidate\x18\x0f \x01(\x0b\x32\'.livekit.proto.RtcStats.RemoteCandidateH\x00\x12:\n\x0b\x63\x65rtificate\x18\x10 \x01(\x0b\x32#.livekit.proto.RtcStats.CertificateH\x00\x12\x30\n\x06stream\x18\x11 \x01(\x0b\x32\x1e.livekit.proto.RtcStats.StreamH\x00\x12.\n\x05track\x18\x12 \x01(\x0b\x32\x1d.livekit.proto.RtcStats.TrackH\x00\x1a[\n\x05\x43odec\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12(\n\x05\x63odec\x18\x02 \x02(\x0b\x32\x19.livekit.proto.CodecStats\x1a\xd5\x01\n\nInboundRtp\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12\x37\n\x08received\x18\x03 \x02(\x0b\x32%.livekit.proto.ReceivedRtpStreamStats\x12\x35\n\x07inbound\x18\x04 \x02(\x0b\x32$.livekit.proto.InboundRtpStreamStats\x1a\xd0\x01\n\x0bOutboundRtp\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12/\n\x04sent\x18\x03 \x02(\x0b\x32!.livekit.proto.SentRtpStreamStats\x12\x37\n\x08outbound\x18\x04 \x02(\x0b\x32%.livekit.proto.OutboundRtpStreamStats\x1a\xe8\x01\n\x10RemoteInboundRtp\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12\x37\n\x08received\x18\x03 \x02(\x0b\x32%.livekit.proto.ReceivedRtpStreamStats\x12\x42\n\x0eremote_inbound\x18\x04 \x02(\x0b\x32*.livekit.proto.RemoteInboundRtpStreamStats\x1a\xe3\x01\n\x11RemoteOutboundRtp\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12-\n\x06stream\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.RtpStreamStats\x12/\n\x04sent\x18\x03 \x02(\x0b\x32!.livekit.proto.SentRtpStreamStats\x12\x44\n\x0fremote_outbound\x18\x04 \x02(\x0b\x32+.livekit.proto.RemoteOutboundRtpStreamStats\x1a\xc8\x01\n\x0bMediaSource\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12/\n\x06source\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.MediaSourceStats\x12.\n\x05\x61udio\x18\x03 \x02(\x0b\x32\x1f.livekit.proto.AudioSourceStats\x12.\n\x05video\x18\x04 \x02(\x0b\x32\x1f.livekit.proto.VideoSourceStats\x1aq\n\x0cMediaPlayout\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x37\n\raudio_playout\x18\x02 \x02(\x0b\x32 .livekit.proto.AudioPlayoutStats\x1aj\n\x0ePeerConnection\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12.\n\x02pc\x18\x02 \x02(\x0b\x32\".livekit.proto.PeerConnectionStats\x1a\x64\n\x0b\x44\x61taChannel\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12+\n\x02\x64\x63\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.DataChannelStats\x1ag\n\tTransport\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x30\n\ttransport\x18\x02 \x02(\x0b\x32\x1d.livekit.proto.TransportStats\x1at\n\rCandidatePair\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x39\n\x0e\x63\x61ndidate_pair\x18\x02 \x02(\x0b\x32!.livekit.proto.CandidatePairStats\x1ao\n\x0eLocalCandidate\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x33\n\tcandidate\x18\x02 \x02(\x0b\x32 .livekit.proto.IceCandidateStats\x1ap\n\x0fRemoteCandidate\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x33\n\tcandidate\x18\x02 \x02(\x0b\x32 .livekit.proto.IceCandidateStats\x1am\n\x0b\x43\x65rtificate\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12\x34\n\x0b\x63\x65rtificate\x18\x02 \x02(\x0b\x32\x1f.livekit.proto.CertificateStats\x1a^\n\x06Stream\x12(\n\x03rtc\x18\x01 \x02(\x0b\x32\x1b.livekit.proto.RtcStatsData\x12*\n\x06stream\x18\x02 \x02(\x0b\x32\x1a.livekit.proto.StreamStats\x1a\x07\n\x05TrackB\x07\n\x05stats\"-\n\x0cRtcStatsData\x12\n\n\x02id\x18\x01 \x02(\t\x12\x11\n\ttimestamp\x18\x02 \x02(\x03\"\x88\x01\n\nCodecStats\x12\x14\n\x0cpayload_type\x18\x01 \x02(\r\x12\x14\n\x0ctransport_id\x18\x02 \x02(\t\x12\x11\n\tmime_type\x18\x03 \x02(\t\x12\x12\n\nclock_rate\x18\x04 \x02(\r\x12\x10\n\x08\x63hannels\x18\x05 \x02(\r\x12\x15\n\rsdp_fmtp_line\x18\x06 \x02(\t\"T\n\x0eRtpStreamStats\x12\x0c\n\x04ssrc\x18\x01 \x02(\r\x12\x0c\n\x04kind\x18\x02 \x02(\t\x12\x14\n\x0ctransport_id\x18\x03 \x02(\t\x12\x10\n\x08\x63odec_id\x18\x04 \x02(\t\"X\n\x16ReceivedRtpStreamStats\x12\x18\n\x10packets_received\x18\x01 \x02(\x04\x12\x14\n\x0cpackets_lost\x18\x02 \x02(\x03\x12\x0e\n\x06jitter\x18\x03 \x02(\x01\"\x82\x0c\n\x15InboundRtpStreamStats\x12\x18\n\x10track_identifier\x18\x01 \x02(\t\x12\x0b\n\x03mid\x18\x02 \x02(\t\x12\x11\n\tremote_id\x18\x03 \x02(\t\x12\x16\n\x0e\x66rames_decoded\x18\x04 \x02(\r\x12\x1a\n\x12key_frames_decoded\x18\x05 \x02(\r\x12\x17\n\x0f\x66rames_rendered\x18\x06 \x02(\r\x12\x16\n\x0e\x66rames_dropped\x18\x07 \x02(\r\x12\x13\n\x0b\x66rame_width\x18\x08 \x02(\r\x12\x14\n\x0c\x66rame_height\x18\t \x02(\r\x12\x19\n\x11\x66rames_per_second\x18\n \x02(\x01\x12\x0e\n\x06qp_sum\x18\x0b \x02(\x04\x12\x19\n\x11total_decode_time\x18\x0c \x02(\x01\x12\x1f\n\x17total_inter_frame_delay\x18\r \x02(\x01\x12\'\n\x1ftotal_squared_inter_frame_delay\x18\x0e \x02(\x01\x12\x13\n\x0bpause_count\x18\x0f \x02(\r\x12\x1c\n\x14total_pause_duration\x18\x10 \x02(\x01\x12\x14\n\x0c\x66reeze_count\x18\x11 \x02(\r\x12\x1d\n\x15total_freeze_duration\x18\x12 \x02(\x01\x12&\n\x1elast_packet_received_timestamp\x18\x13 \x02(\x01\x12\x1d\n\x15header_bytes_received\x18\x14 \x02(\x04\x12\x19\n\x11packets_discarded\x18\x15 \x02(\x04\x12\x1a\n\x12\x66\x65\x63_bytes_received\x18\x16 \x02(\x04\x12\x1c\n\x14\x66\x65\x63_packets_received\x18\x17 \x02(\x04\x12\x1d\n\x15\x66\x65\x63_packets_discarded\x18\x18 \x02(\x04\x12\x16\n\x0e\x62ytes_received\x18\x19 \x02(\x04\x12\x12\n\nnack_count\x18\x1a \x02(\r\x12\x11\n\tfir_count\x18\x1b \x02(\r\x12\x11\n\tpli_count\x18\x1c \x02(\r\x12\x1e\n\x16total_processing_delay\x18\x1d \x02(\x01\x12#\n\x1b\x65stimated_playout_timestamp\x18\x1e \x02(\x01\x12\x1b\n\x13jitter_buffer_delay\x18\x1f \x02(\x01\x12\"\n\x1ajitter_buffer_target_delay\x18 \x02(\x01\x12#\n\x1bjitter_buffer_emitted_count\x18! \x02(\x04\x12#\n\x1bjitter_buffer_minimum_delay\x18\" \x02(\x01\x12\x1e\n\x16total_samples_received\x18# \x02(\x04\x12\x19\n\x11\x63oncealed_samples\x18$ \x02(\x04\x12 \n\x18silent_concealed_samples\x18% \x02(\x04\x12\x1a\n\x12\x63oncealment_events\x18& \x02(\x04\x12)\n!inserted_samples_for_deceleration\x18\' \x02(\x04\x12(\n removed_samples_for_acceleration\x18( \x02(\x04\x12\x13\n\x0b\x61udio_level\x18) \x02(\x01\x12\x1a\n\x12total_audio_energy\x18* \x02(\x01\x12\x1e\n\x16total_samples_duration\x18+ \x02(\x01\x12\x17\n\x0f\x66rames_received\x18, \x02(\x04\x12\x1e\n\x16\x64\x65\x63oder_implementation\x18- \x02(\t\x12\x12\n\nplayout_id\x18. \x02(\t\x12\x1f\n\x17power_efficient_decoder\x18/ \x02(\x08\x12.\n&frames_assembled_from_multiple_packets\x18\x30 \x02(\x04\x12\x1b\n\x13total_assembly_time\x18\x31 \x02(\x01\x12&\n\x1eretransmitted_packets_received\x18\x32 \x02(\x04\x12$\n\x1cretransmitted_bytes_received\x18\x33 \x02(\x04\x12\x10\n\x08rtx_ssrc\x18\x34 \x02(\r\x12\x10\n\x08\x66\x65\x63_ssrc\x18\x35 \x02(\r\">\n\x12SentRtpStreamStats\x12\x14\n\x0cpackets_sent\x18\x01 \x02(\x04\x12\x12\n\nbytes_sent\x18\x02 \x02(\x04\"\xd1\x07\n\x16OutboundRtpStreamStats\x12\x0b\n\x03mid\x18\x01 \x02(\t\x12\x17\n\x0fmedia_source_id\x18\x02 \x02(\t\x12\x11\n\tremote_id\x18\x03 \x02(\t\x12\x0b\n\x03rid\x18\x04 \x02(\t\x12\x19\n\x11header_bytes_sent\x18\x05 \x02(\x04\x12\"\n\x1aretransmitted_packets_sent\x18\x06 \x02(\x04\x12 \n\x18retransmitted_bytes_sent\x18\x07 \x02(\x04\x12\x10\n\x08rtx_ssrc\x18\x08 \x02(\r\x12\x16\n\x0etarget_bitrate\x18\t \x02(\x01\x12\"\n\x1atotal_encoded_bytes_target\x18\n \x02(\x04\x12\x13\n\x0b\x66rame_width\x18\x0b \x02(\r\x12\x14\n\x0c\x66rame_height\x18\x0c \x02(\r\x12\x19\n\x11\x66rames_per_second\x18\r \x02(\x01\x12\x13\n\x0b\x66rames_sent\x18\x0e \x02(\r\x12\x18\n\x10huge_frames_sent\x18\x0f \x02(\r\x12\x16\n\x0e\x66rames_encoded\x18\x10 \x02(\r\x12\x1a\n\x12key_frames_encoded\x18\x11 \x02(\r\x12\x0e\n\x06qp_sum\x18\x12 \x02(\x04\x12\x19\n\x11total_encode_time\x18\x13 \x02(\x01\x12\x1f\n\x17total_packet_send_delay\x18\x14 \x02(\x01\x12I\n\x19quality_limitation_reason\x18\x15 \x02(\x0e\x32&.livekit.proto.QualityLimitationReason\x12k\n\x1cquality_limitation_durations\x18\x16 \x03(\x0b\x32\x45.livekit.proto.OutboundRtpStreamStats.QualityLimitationDurationsEntry\x12-\n%quality_limitation_resolution_changes\x18\x17 \x02(\r\x12\x12\n\nnack_count\x18\x18 \x02(\r\x12\x11\n\tfir_count\x18\x19 \x02(\r\x12\x11\n\tpli_count\x18\x1a \x02(\r\x12\x1e\n\x16\x65ncoder_implementation\x18\x1b \x02(\t\x12\x1f\n\x17power_efficient_encoder\x18\x1c \x02(\x08\x12\x0e\n\x06\x61\x63tive\x18\x1d \x02(\x08\x12\x18\n\x10scalability_mode\x18\x1e \x02(\t\x1a\x41\n\x1fQualityLimitationDurationsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x01:\x02\x38\x01\"\xa4\x01\n\x1bRemoteInboundRtpStreamStats\x12\x10\n\x08local_id\x18\x01 \x02(\t\x12\x17\n\x0fround_trip_time\x18\x02 \x02(\x01\x12\x1d\n\x15total_round_trip_time\x18\x03 \x02(\x01\x12\x15\n\rfraction_lost\x18\x04 \x02(\x01\x12$\n\x1cround_trip_time_measurements\x18\x05 \x02(\x04\"\xbe\x01\n\x1cRemoteOutboundRtpStreamStats\x12\x10\n\x08local_id\x18\x01 \x02(\t\x12\x18\n\x10remote_timestamp\x18\x02 \x02(\x01\x12\x14\n\x0creports_sent\x18\x03 \x02(\x04\x12\x17\n\x0fround_trip_time\x18\x04 \x02(\x01\x12\x1d\n\x15total_round_trip_time\x18\x05 \x02(\x01\x12$\n\x1cround_trip_time_measurements\x18\x06 \x02(\x04\":\n\x10MediaSourceStats\x12\x18\n\x10track_identifier\x18\x01 \x02(\t\x12\x0c\n\x04kind\x18\x02 \x02(\t\"\xa2\x02\n\x10\x41udioSourceStats\x12\x13\n\x0b\x61udio_level\x18\x01 \x02(\x01\x12\x1a\n\x12total_audio_energy\x18\x02 \x02(\x01\x12\x1e\n\x16total_samples_duration\x18\x03 \x02(\x01\x12\x18\n\x10\x65\x63ho_return_loss\x18\x04 \x02(\x01\x12$\n\x1c\x65\x63ho_return_loss_enhancement\x18\x05 \x02(\x01\x12 \n\x18\x64ropped_samples_duration\x18\x06 \x02(\x01\x12\x1e\n\x16\x64ropped_samples_events\x18\x07 \x02(\r\x12\x1b\n\x13total_capture_delay\x18\x08 \x02(\x01\x12\x1e\n\x16total_samples_captured\x18\t \x02(\x04\"\\\n\x10VideoSourceStats\x12\r\n\x05width\x18\x01 \x02(\r\x12\x0e\n\x06height\x18\x02 \x02(\r\x12\x0e\n\x06\x66rames\x18\x03 \x02(\r\x12\x19\n\x11\x66rames_per_second\x18\x04 \x02(\x01\"\xc5\x01\n\x11\x41udioPlayoutStats\x12\x0c\n\x04kind\x18\x01 \x02(\t\x12$\n\x1csynthesized_samples_duration\x18\x02 \x02(\x01\x12\"\n\x1asynthesized_samples_events\x18\x03 \x02(\r\x12\x1e\n\x16total_samples_duration\x18\x04 \x02(\x01\x12\x1b\n\x13total_playout_delay\x18\x05 \x02(\x01\x12\x1b\n\x13total_samples_count\x18\x06 \x02(\x04\"Q\n\x13PeerConnectionStats\x12\x1c\n\x14\x64\x61ta_channels_opened\x18\x01 \x02(\r\x12\x1c\n\x14\x64\x61ta_channels_closed\x18\x02 \x02(\r\"\xe2\x01\n\x10\x44\x61taChannelStats\x12\r\n\x05label\x18\x01 \x02(\t\x12\x10\n\x08protocol\x18\x02 \x02(\t\x12\x1f\n\x17\x64\x61ta_channel_identifier\x18\x03 \x02(\x05\x12.\n\x05state\x18\x04 \x01(\x0e\x32\x1f.livekit.proto.DataChannelState\x12\x15\n\rmessages_sent\x18\x05 \x02(\r\x12\x12\n\nbytes_sent\x18\x06 \x02(\x04\x12\x19\n\x11messages_received\x18\x07 \x02(\r\x12\x16\n\x0e\x62ytes_received\x18\x08 \x02(\x04\"\x9c\x04\n\x0eTransportStats\x12\x14\n\x0cpackets_sent\x18\x01 \x02(\x04\x12\x18\n\x10packets_received\x18\x02 \x02(\x04\x12\x12\n\nbytes_sent\x18\x03 \x02(\x04\x12\x16\n\x0e\x62ytes_received\x18\x04 \x02(\x04\x12(\n\x08ice_role\x18\x05 \x02(\x0e\x32\x16.livekit.proto.IceRole\x12#\n\x1bice_local_username_fragment\x18\x06 \x02(\t\x12\x35\n\ndtls_state\x18\x07 \x01(\x0e\x32!.livekit.proto.DtlsTransportState\x12\x33\n\tice_state\x18\x08 \x01(\x0e\x32 .livekit.proto.IceTransportState\x12\"\n\x1aselected_candidate_pair_id\x18\t \x02(\t\x12\x1c\n\x14local_certificate_id\x18\n \x02(\t\x12\x1d\n\x15remote_certificate_id\x18\x0b \x02(\t\x12\x13\n\x0btls_version\x18\x0c \x02(\t\x12\x13\n\x0b\x64tls_cipher\x18\r \x02(\t\x12*\n\tdtls_role\x18\x0e \x02(\x0e\x32\x17.livekit.proto.DtlsRole\x12\x13\n\x0bsrtp_cipher\x18\x0f \x02(\t\x12\'\n\x1fselected_candidate_pair_changes\x18\x10 \x02(\r\"\xa4\x05\n\x12\x43\x61ndidatePairStats\x12\x14\n\x0ctransport_id\x18\x01 \x02(\t\x12\x1a\n\x12local_candidate_id\x18\x02 \x02(\t\x12\x1b\n\x13remote_candidate_id\x18\x03 \x02(\t\x12\x33\n\x05state\x18\x04 \x01(\x0e\x32$.livekit.proto.IceCandidatePairState\x12\x11\n\tnominated\x18\x05 \x02(\x08\x12\x14\n\x0cpackets_sent\x18\x06 \x02(\x04\x12\x18\n\x10packets_received\x18\x07 \x02(\x04\x12\x12\n\nbytes_sent\x18\x08 \x02(\x04\x12\x16\n\x0e\x62ytes_received\x18\t \x02(\x04\x12\"\n\x1alast_packet_sent_timestamp\x18\n \x02(\x01\x12&\n\x1elast_packet_received_timestamp\x18\x0b \x02(\x01\x12\x1d\n\x15total_round_trip_time\x18\x0c \x02(\x01\x12\x1f\n\x17\x63urrent_round_trip_time\x18\r \x02(\x01\x12\"\n\x1a\x61vailable_outgoing_bitrate\x18\x0e \x02(\x01\x12\"\n\x1a\x61vailable_incoming_bitrate\x18\x0f \x02(\x01\x12\x19\n\x11requests_received\x18\x10 \x02(\x04\x12\x15\n\rrequests_sent\x18\x11 \x02(\x04\x12\x1a\n\x12responses_received\x18\x12 \x02(\x04\x12\x16\n\x0eresponses_sent\x18\x13 \x02(\x04\x12\x1d\n\x15\x63onsent_requests_sent\x18\x14 \x02(\x04\x12!\n\x19packets_discarded_on_send\x18\x15 \x02(\r\x12\x1f\n\x17\x62ytes_discarded_on_send\x18\x16 \x02(\x04\"\x89\x03\n\x11IceCandidateStats\x12\x14\n\x0ctransport_id\x18\x01 \x02(\t\x12\x0f\n\x07\x61\x64\x64ress\x18\x02 \x02(\t\x12\x0c\n\x04port\x18\x03 \x02(\x05\x12\x10\n\x08protocol\x18\x04 \x02(\t\x12\x37\n\x0e\x63\x61ndidate_type\x18\x05 \x01(\x0e\x32\x1f.livekit.proto.IceCandidateType\x12\x10\n\x08priority\x18\x06 \x02(\x05\x12\x0b\n\x03url\x18\x07 \x02(\t\x12\x41\n\x0erelay_protocol\x18\x08 \x01(\x0e\x32).livekit.proto.IceServerTransportProtocol\x12\x12\n\nfoundation\x18\t \x02(\t\x12\x17\n\x0frelated_address\x18\n \x02(\t\x12\x14\n\x0crelated_port\x18\x0b \x02(\x05\x12\x19\n\x11username_fragment\x18\x0c \x02(\t\x12\x34\n\x08tcp_type\x18\r \x01(\x0e\x32\".livekit.proto.IceTcpCandidateType\"\x81\x01\n\x10\x43\x65rtificateStats\x12\x13\n\x0b\x66ingerprint\x18\x01 \x02(\t\x12\x1d\n\x15\x66ingerprint_algorithm\x18\x02 \x02(\t\x12\x1a\n\x12\x62\x61se64_certificate\x18\x03 \x02(\t\x12\x1d\n\x15issuer_certificate_id\x18\x04 \x02(\t\"4\n\x0bStreamStats\x12\n\n\x02id\x18\x01 \x02(\t\x12\x19\n\x11stream_identifier\x18\x02 \x02(\t*Q\n\x10\x44\x61taChannelState\x12\x11\n\rDC_CONNECTING\x10\x00\x12\x0b\n\x07\x44\x43_OPEN\x10\x01\x12\x0e\n\nDC_CLOSING\x10\x02\x12\r\n\tDC_CLOSED\x10\x03*r\n\x17QualityLimitationReason\x12\x13\n\x0fLIMITATION_NONE\x10\x00\x12\x12\n\x0eLIMITATION_CPU\x10\x01\x12\x18\n\x14LIMITATION_BANDWIDTH\x10\x02\x12\x14\n\x10LIMITATION_OTHER\x10\x03*C\n\x07IceRole\x12\x0f\n\x0bICE_UNKNOWN\x10\x00\x12\x13\n\x0fICE_CONTROLLING\x10\x01\x12\x12\n\x0eICE_CONTROLLED\x10\x02*\x9f\x01\n\x12\x44tlsTransportState\x12\x16\n\x12\x44TLS_TRANSPORT_NEW\x10\x00\x12\x1d\n\x19\x44TLS_TRANSPORT_CONNECTING\x10\x01\x12\x1c\n\x18\x44TLS_TRANSPORT_CONNECTED\x10\x02\x12\x19\n\x15\x44TLS_TRANSPORT_CLOSED\x10\x03\x12\x19\n\x15\x44TLS_TRANSPORT_FAILED\x10\x04*\xd4\x01\n\x11IceTransportState\x12\x15\n\x11ICE_TRANSPORT_NEW\x10\x00\x12\x1a\n\x16ICE_TRANSPORT_CHECKING\x10\x01\x12\x1b\n\x17ICE_TRANSPORT_CONNECTED\x10\x02\x12\x1b\n\x17ICE_TRANSPORT_COMPLETED\x10\x03\x12\x1e\n\x1aICE_TRANSPORT_DISCONNECTED\x10\x04\x12\x18\n\x14ICE_TRANSPORT_FAILED\x10\x05\x12\x18\n\x14ICE_TRANSPORT_CLOSED\x10\x06*>\n\x08\x44tlsRole\x12\x0f\n\x0b\x44TLS_CLIENT\x10\x00\x12\x0f\n\x0b\x44TLS_SERVER\x10\x01\x12\x10\n\x0c\x44TLS_UNKNOWN\x10\x02*u\n\x15IceCandidatePairState\x12\x0f\n\x0bPAIR_FROZEN\x10\x00\x12\x10\n\x0cPAIR_WAITING\x10\x01\x12\x14\n\x10PAIR_IN_PROGRESS\x10\x02\x12\x0f\n\x0bPAIR_FAILED\x10\x03\x12\x12\n\x0ePAIR_SUCCEEDED\x10\x04*=\n\x10IceCandidateType\x12\x08\n\x04HOST\x10\x00\x12\t\n\x05SRFLX\x10\x01\x12\t\n\x05PRFLX\x10\x02\x12\t\n\x05RELAY\x10\x03*U\n\x1aIceServerTransportProtocol\x12\x11\n\rTRANSPORT_UDP\x10\x00\x12\x11\n\rTRANSPORT_TCP\x10\x01\x12\x11\n\rTRANSPORT_TLS\x10\x02*T\n\x13IceTcpCandidateType\x12\x14\n\x10\x43\x41NDIDATE_ACTIVE\x10\x00\x12\x15\n\x11\x43\x41NDIDATE_PASSIVE\x10\x01\x12\x10\n\x0c\x43\x41NDIDATE_SO\x10\x02\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -24,96 +24,100 @@ _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._options = None _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._serialized_options = b'8\001' - _globals['_DATACHANNELSTATE']._serialized_start=9217 - _globals['_DATACHANNELSTATE']._serialized_end=9298 - _globals['_QUALITYLIMITATIONREASON']._serialized_start=9300 - _globals['_QUALITYLIMITATIONREASON']._serialized_end=9414 - _globals['_ICEROLE']._serialized_start=9416 - _globals['_ICEROLE']._serialized_end=9483 - _globals['_DTLSTRANSPORTSTATE']._serialized_start=9486 - _globals['_DTLSTRANSPORTSTATE']._serialized_end=9645 - _globals['_ICETRANSPORTSTATE']._serialized_start=9648 - _globals['_ICETRANSPORTSTATE']._serialized_end=9860 - _globals['_DTLSROLE']._serialized_start=9862 - _globals['_DTLSROLE']._serialized_end=9924 - _globals['_ICECANDIDATEPAIRSTATE']._serialized_start=9926 - _globals['_ICECANDIDATEPAIRSTATE']._serialized_end=10043 - _globals['_ICECANDIDATETYPE']._serialized_start=10045 - _globals['_ICECANDIDATETYPE']._serialized_end=10106 - _globals['_ICESERVERTRANSPORTPROTOCOL']._serialized_start=10108 - _globals['_ICESERVERTRANSPORTPROTOCOL']._serialized_end=10193 - _globals['_ICETCPCANDIDATETYPE']._serialized_start=10195 - _globals['_ICETCPCANDIDATETYPE']._serialized_end=10279 + _globals['_DATACHANNELSTATE']._serialized_start=9282 + _globals['_DATACHANNELSTATE']._serialized_end=9363 + _globals['_QUALITYLIMITATIONREASON']._serialized_start=9365 + _globals['_QUALITYLIMITATIONREASON']._serialized_end=9479 + _globals['_ICEROLE']._serialized_start=9481 + _globals['_ICEROLE']._serialized_end=9548 + _globals['_DTLSTRANSPORTSTATE']._serialized_start=9551 + _globals['_DTLSTRANSPORTSTATE']._serialized_end=9710 + _globals['_ICETRANSPORTSTATE']._serialized_start=9713 + _globals['_ICETRANSPORTSTATE']._serialized_end=9925 + _globals['_DTLSROLE']._serialized_start=9927 + _globals['_DTLSROLE']._serialized_end=9989 + _globals['_ICECANDIDATEPAIRSTATE']._serialized_start=9991 + _globals['_ICECANDIDATEPAIRSTATE']._serialized_end=10108 + _globals['_ICECANDIDATETYPE']._serialized_start=10110 + _globals['_ICECANDIDATETYPE']._serialized_end=10171 + _globals['_ICESERVERTRANSPORTPROTOCOL']._serialized_start=10173 + _globals['_ICESERVERTRANSPORTPROTOCOL']._serialized_end=10258 + _globals['_ICETCPCANDIDATETYPE']._serialized_start=10260 + _globals['_ICETCPCANDIDATETYPE']._serialized_end=10344 _globals['_RTCSTATS']._serialized_start=31 - _globals['_RTCSTATS']._serialized_end=3064 - _globals['_RTCSTATS_CODEC']._serialized_start=974 - _globals['_RTCSTATS_CODEC']._serialized_end=1065 - _globals['_RTCSTATS_INBOUNDRTP']._serialized_start=1068 - _globals['_RTCSTATS_INBOUNDRTP']._serialized_end=1281 - _globals['_RTCSTATS_OUTBOUNDRTP']._serialized_start=1284 - _globals['_RTCSTATS_OUTBOUNDRTP']._serialized_end=1492 - _globals['_RTCSTATS_REMOTEINBOUNDRTP']._serialized_start=1495 - _globals['_RTCSTATS_REMOTEINBOUNDRTP']._serialized_end=1727 - _globals['_RTCSTATS_REMOTEOUTBOUNDRTP']._serialized_start=1730 - _globals['_RTCSTATS_REMOTEOUTBOUNDRTP']._serialized_end=1957 - _globals['_RTCSTATS_MEDIASOURCE']._serialized_start=1960 - _globals['_RTCSTATS_MEDIASOURCE']._serialized_end=2160 - _globals['_RTCSTATS_MEDIAPLAYOUT']._serialized_start=2162 - _globals['_RTCSTATS_MEDIAPLAYOUT']._serialized_end=2275 - _globals['_RTCSTATS_PEERCONNECTION']._serialized_start=2277 - _globals['_RTCSTATS_PEERCONNECTION']._serialized_end=2383 - _globals['_RTCSTATS_DATACHANNEL']._serialized_start=2385 - _globals['_RTCSTATS_DATACHANNEL']._serialized_end=2485 - _globals['_RTCSTATS_TRANSPORT']._serialized_start=2487 - _globals['_RTCSTATS_TRANSPORT']._serialized_end=2590 - _globals['_RTCSTATS_CANDIDATEPAIR']._serialized_start=2592 - _globals['_RTCSTATS_CANDIDATEPAIR']._serialized_end=2708 - _globals['_RTCSTATS_LOCALCANDIDATE']._serialized_start=2710 - _globals['_RTCSTATS_LOCALCANDIDATE']._serialized_end=2821 - _globals['_RTCSTATS_REMOTECANDIDATE']._serialized_start=2823 - _globals['_RTCSTATS_REMOTECANDIDATE']._serialized_end=2935 - _globals['_RTCSTATS_CERTIFICATE']._serialized_start=2937 - _globals['_RTCSTATS_CERTIFICATE']._serialized_end=3046 - _globals['_RTCSTATS_TRACK']._serialized_start=3048 - _globals['_RTCSTATS_TRACK']._serialized_end=3055 - _globals['_RTCSTATSDATA']._serialized_start=3066 - _globals['_RTCSTATSDATA']._serialized_end=3111 - _globals['_CODECSTATS']._serialized_start=3114 - _globals['_CODECSTATS']._serialized_end=3250 - _globals['_RTPSTREAMSTATS']._serialized_start=3252 - _globals['_RTPSTREAMSTATS']._serialized_end=3336 - _globals['_RECEIVEDRTPSTREAMSTATS']._serialized_start=3338 - _globals['_RECEIVEDRTPSTREAMSTATS']._serialized_end=3426 - _globals['_INBOUNDRTPSTREAMSTATS']._serialized_start=3429 - _globals['_INBOUNDRTPSTREAMSTATS']._serialized_end=4967 - _globals['_SENTRTPSTREAMSTATS']._serialized_start=4969 - _globals['_SENTRTPSTREAMSTATS']._serialized_end=5031 - _globals['_OUTBOUNDRTPSTREAMSTATS']._serialized_start=5034 - _globals['_OUTBOUNDRTPSTREAMSTATS']._serialized_end=6011 - _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._serialized_start=5946 - _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._serialized_end=6011 - _globals['_REMOTEINBOUNDRTPSTREAMSTATS']._serialized_start=6014 - _globals['_REMOTEINBOUNDRTPSTREAMSTATS']._serialized_end=6178 - _globals['_REMOTEOUTBOUNDRTPSTREAMSTATS']._serialized_start=6181 - _globals['_REMOTEOUTBOUNDRTPSTREAMSTATS']._serialized_end=6371 - _globals['_MEDIASOURCESTATS']._serialized_start=6373 - _globals['_MEDIASOURCESTATS']._serialized_end=6431 - _globals['_AUDIOSOURCESTATS']._serialized_start=6434 - _globals['_AUDIOSOURCESTATS']._serialized_end=6724 - _globals['_VIDEOSOURCESTATS']._serialized_start=6726 - _globals['_VIDEOSOURCESTATS']._serialized_end=6818 - _globals['_AUDIOPLAYOUTSTATS']._serialized_start=6821 - _globals['_AUDIOPLAYOUTSTATS']._serialized_end=7018 - _globals['_PEERCONNECTIONSTATS']._serialized_start=7020 - _globals['_PEERCONNECTIONSTATS']._serialized_end=7101 - _globals['_DATACHANNELSTATS']._serialized_start=7104 - _globals['_DATACHANNELSTATS']._serialized_end=7345 - _globals['_TRANSPORTSTATS']._serialized_start=7348 - _globals['_TRANSPORTSTATS']._serialized_end=7927 - _globals['_CANDIDATEPAIRSTATS']._serialized_start=7930 - _globals['_CANDIDATEPAIRSTATS']._serialized_end=8621 - _globals['_ICECANDIDATESTATS']._serialized_start=8624 - _globals['_ICECANDIDATESTATS']._serialized_end=9083 - _globals['_CERTIFICATESTATS']._serialized_start=9086 - _globals['_CERTIFICATESTATS']._serialized_end=9215 + _globals['_RTCSTATS']._serialized_end=3210 + _globals['_RTCSTATS_CODEC']._serialized_start=1024 + _globals['_RTCSTATS_CODEC']._serialized_end=1115 + _globals['_RTCSTATS_INBOUNDRTP']._serialized_start=1118 + _globals['_RTCSTATS_INBOUNDRTP']._serialized_end=1331 + _globals['_RTCSTATS_OUTBOUNDRTP']._serialized_start=1334 + _globals['_RTCSTATS_OUTBOUNDRTP']._serialized_end=1542 + _globals['_RTCSTATS_REMOTEINBOUNDRTP']._serialized_start=1545 + _globals['_RTCSTATS_REMOTEINBOUNDRTP']._serialized_end=1777 + _globals['_RTCSTATS_REMOTEOUTBOUNDRTP']._serialized_start=1780 + _globals['_RTCSTATS_REMOTEOUTBOUNDRTP']._serialized_end=2007 + _globals['_RTCSTATS_MEDIASOURCE']._serialized_start=2010 + _globals['_RTCSTATS_MEDIASOURCE']._serialized_end=2210 + _globals['_RTCSTATS_MEDIAPLAYOUT']._serialized_start=2212 + _globals['_RTCSTATS_MEDIAPLAYOUT']._serialized_end=2325 + _globals['_RTCSTATS_PEERCONNECTION']._serialized_start=2327 + _globals['_RTCSTATS_PEERCONNECTION']._serialized_end=2433 + _globals['_RTCSTATS_DATACHANNEL']._serialized_start=2435 + _globals['_RTCSTATS_DATACHANNEL']._serialized_end=2535 + _globals['_RTCSTATS_TRANSPORT']._serialized_start=2537 + _globals['_RTCSTATS_TRANSPORT']._serialized_end=2640 + _globals['_RTCSTATS_CANDIDATEPAIR']._serialized_start=2642 + _globals['_RTCSTATS_CANDIDATEPAIR']._serialized_end=2758 + _globals['_RTCSTATS_LOCALCANDIDATE']._serialized_start=2760 + _globals['_RTCSTATS_LOCALCANDIDATE']._serialized_end=2871 + _globals['_RTCSTATS_REMOTECANDIDATE']._serialized_start=2873 + _globals['_RTCSTATS_REMOTECANDIDATE']._serialized_end=2985 + _globals['_RTCSTATS_CERTIFICATE']._serialized_start=2987 + _globals['_RTCSTATS_CERTIFICATE']._serialized_end=3096 + _globals['_RTCSTATS_STREAM']._serialized_start=3098 + _globals['_RTCSTATS_STREAM']._serialized_end=3192 + _globals['_RTCSTATS_TRACK']._serialized_start=3194 + _globals['_RTCSTATS_TRACK']._serialized_end=3201 + _globals['_RTCSTATSDATA']._serialized_start=3212 + _globals['_RTCSTATSDATA']._serialized_end=3257 + _globals['_CODECSTATS']._serialized_start=3260 + _globals['_CODECSTATS']._serialized_end=3396 + _globals['_RTPSTREAMSTATS']._serialized_start=3398 + _globals['_RTPSTREAMSTATS']._serialized_end=3482 + _globals['_RECEIVEDRTPSTREAMSTATS']._serialized_start=3484 + _globals['_RECEIVEDRTPSTREAMSTATS']._serialized_end=3572 + _globals['_INBOUNDRTPSTREAMSTATS']._serialized_start=3575 + _globals['_INBOUNDRTPSTREAMSTATS']._serialized_end=5113 + _globals['_SENTRTPSTREAMSTATS']._serialized_start=5115 + _globals['_SENTRTPSTREAMSTATS']._serialized_end=5177 + _globals['_OUTBOUNDRTPSTREAMSTATS']._serialized_start=5180 + _globals['_OUTBOUNDRTPSTREAMSTATS']._serialized_end=6157 + _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._serialized_start=6092 + _globals['_OUTBOUNDRTPSTREAMSTATS_QUALITYLIMITATIONDURATIONSENTRY']._serialized_end=6157 + _globals['_REMOTEINBOUNDRTPSTREAMSTATS']._serialized_start=6160 + _globals['_REMOTEINBOUNDRTPSTREAMSTATS']._serialized_end=6324 + _globals['_REMOTEOUTBOUNDRTPSTREAMSTATS']._serialized_start=6327 + _globals['_REMOTEOUTBOUNDRTPSTREAMSTATS']._serialized_end=6517 + _globals['_MEDIASOURCESTATS']._serialized_start=6519 + _globals['_MEDIASOURCESTATS']._serialized_end=6577 + _globals['_AUDIOSOURCESTATS']._serialized_start=6580 + _globals['_AUDIOSOURCESTATS']._serialized_end=6870 + _globals['_VIDEOSOURCESTATS']._serialized_start=6872 + _globals['_VIDEOSOURCESTATS']._serialized_end=6964 + _globals['_AUDIOPLAYOUTSTATS']._serialized_start=6967 + _globals['_AUDIOPLAYOUTSTATS']._serialized_end=7164 + _globals['_PEERCONNECTIONSTATS']._serialized_start=7166 + _globals['_PEERCONNECTIONSTATS']._serialized_end=7247 + _globals['_DATACHANNELSTATS']._serialized_start=7250 + _globals['_DATACHANNELSTATS']._serialized_end=7476 + _globals['_TRANSPORTSTATS']._serialized_start=7479 + _globals['_TRANSPORTSTATS']._serialized_end=8019 + _globals['_CANDIDATEPAIRSTATS']._serialized_start=8022 + _globals['_CANDIDATEPAIRSTATS']._serialized_end=8698 + _globals['_ICECANDIDATESTATS']._serialized_start=8701 + _globals['_ICECANDIDATESTATS']._serialized_end=9094 + _globals['_CERTIFICATESTATS']._serialized_start=9097 + _globals['_CERTIFICATESTATS']._serialized_end=9226 + _globals['_STREAMSTATS']._serialized_start=9228 + _globals['_STREAMSTATS']._serialized_end=9280 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/stats_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/stats_pb2.pyi index 82b8ef4b..a77c233e 100644 --- a/livekit-rtc/livekit/rtc/_proto/stats_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/stats_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import collections.abc import google.protobuf.descriptor @@ -223,11 +224,11 @@ CANDIDATE_PASSIVE: IceTcpCandidateType.ValueType # 1 CANDIDATE_SO: IceTcpCandidateType.ValueType # 2 global___IceTcpCandidateType = IceTcpCandidateType -@typing_extensions.final +@typing.final class RtcStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class Codec(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -243,10 +244,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., codec: global___CodecStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["codec", b"codec", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["codec", b"codec", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["codec", b"codec", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["codec", b"codec", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class InboundRtp(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -270,10 +271,10 @@ class RtcStats(google.protobuf.message.Message): received: global___ReceivedRtpStreamStats | None = ..., inbound: global___InboundRtpStreamStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["inbound", b"inbound", "received", b"received", "rtc", b"rtc", "stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["inbound", b"inbound", "received", b"received", "rtc", b"rtc", "stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["inbound", b"inbound", "received", b"received", "rtc", b"rtc", "stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["inbound", b"inbound", "received", b"received", "rtc", b"rtc", "stream", b"stream"]) -> None: ... - @typing_extensions.final + @typing.final class OutboundRtp(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -297,10 +298,10 @@ class RtcStats(google.protobuf.message.Message): sent: global___SentRtpStreamStats | None = ..., outbound: global___OutboundRtpStreamStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["outbound", b"outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["outbound", b"outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["outbound", b"outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["outbound", b"outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> None: ... - @typing_extensions.final + @typing.final class RemoteInboundRtp(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -324,10 +325,10 @@ class RtcStats(google.protobuf.message.Message): received: global___ReceivedRtpStreamStats | None = ..., remote_inbound: global___RemoteInboundRtpStreamStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["received", b"received", "remote_inbound", b"remote_inbound", "rtc", b"rtc", "stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["received", b"received", "remote_inbound", b"remote_inbound", "rtc", b"rtc", "stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["received", b"received", "remote_inbound", b"remote_inbound", "rtc", b"rtc", "stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["received", b"received", "remote_inbound", b"remote_inbound", "rtc", b"rtc", "stream", b"stream"]) -> None: ... - @typing_extensions.final + @typing.final class RemoteOutboundRtp(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -351,10 +352,10 @@ class RtcStats(google.protobuf.message.Message): sent: global___SentRtpStreamStats | None = ..., remote_outbound: global___RemoteOutboundRtpStreamStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["remote_outbound", b"remote_outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["remote_outbound", b"remote_outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["remote_outbound", b"remote_outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["remote_outbound", b"remote_outbound", "rtc", b"rtc", "sent", b"sent", "stream", b"stream"]) -> None: ... - @typing_extensions.final + @typing.final class MediaSource(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -378,10 +379,10 @@ class RtcStats(google.protobuf.message.Message): audio: global___AudioSourceStats | None = ..., video: global___VideoSourceStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["audio", b"audio", "rtc", b"rtc", "source", b"source", "video", b"video"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["audio", b"audio", "rtc", b"rtc", "source", b"source", "video", b"video"]) -> None: ... + def HasField(self, field_name: typing.Literal["audio", b"audio", "rtc", b"rtc", "source", b"source", "video", b"video"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio", b"audio", "rtc", b"rtc", "source", b"source", "video", b"video"]) -> None: ... - @typing_extensions.final + @typing.final class MediaPlayout(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -397,10 +398,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., audio_playout: global___AudioPlayoutStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["audio_playout", b"audio_playout", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["audio_playout", b"audio_playout", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["audio_playout", b"audio_playout", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_playout", b"audio_playout", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class PeerConnection(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -416,10 +417,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., pc: global___PeerConnectionStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["pc", b"pc", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["pc", b"pc", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["pc", b"pc", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["pc", b"pc", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class DataChannel(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -435,10 +436,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., dc: global___DataChannelStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["dc", b"dc", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["dc", b"dc", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["dc", b"dc", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["dc", b"dc", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class Transport(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -454,10 +455,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., transport: global___TransportStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["rtc", b"rtc", "transport", b"transport"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["rtc", b"rtc", "transport", b"transport"]) -> None: ... + def HasField(self, field_name: typing.Literal["rtc", b"rtc", "transport", b"transport"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["rtc", b"rtc", "transport", b"transport"]) -> None: ... - @typing_extensions.final + @typing.final class CandidatePair(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -473,10 +474,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., candidate_pair: global___CandidatePairStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["candidate_pair", b"candidate_pair", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["candidate_pair", b"candidate_pair", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["candidate_pair", b"candidate_pair", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["candidate_pair", b"candidate_pair", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class LocalCandidate(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -492,10 +493,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., candidate: global___IceCandidateStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class RemoteCandidate(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -511,10 +512,10 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., candidate: global___IceCandidateStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["candidate", b"candidate", "rtc", b"rtc"]) -> None: ... - @typing_extensions.final + @typing.final class Certificate(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -530,10 +531,29 @@ class RtcStats(google.protobuf.message.Message): rtc: global___RtcStatsData | None = ..., certificate: global___CertificateStats | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "rtc", b"rtc"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["certificate", b"certificate", "rtc", b"rtc"]) -> None: ... + def HasField(self, field_name: typing.Literal["certificate", b"certificate", "rtc", b"rtc"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["certificate", b"certificate", "rtc", b"rtc"]) -> None: ... + + @typing.final + class Stream(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + RTC_FIELD_NUMBER: builtins.int + STREAM_FIELD_NUMBER: builtins.int + @property + def rtc(self) -> global___RtcStatsData: ... + @property + def stream(self) -> global___StreamStats: ... + def __init__( + self, + *, + rtc: global___RtcStatsData | None = ..., + stream: global___StreamStats | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["rtc", b"rtc", "stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["rtc", b"rtc", "stream", b"stream"]) -> None: ... + + @typing.final class Track(google.protobuf.message.Message): """Deprecated""" @@ -557,6 +577,7 @@ class RtcStats(google.protobuf.message.Message): LOCAL_CANDIDATE_FIELD_NUMBER: builtins.int REMOTE_CANDIDATE_FIELD_NUMBER: builtins.int CERTIFICATE_FIELD_NUMBER: builtins.int + STREAM_FIELD_NUMBER: builtins.int TRACK_FIELD_NUMBER: builtins.int @property def codec(self) -> global___RtcStats.Codec: ... @@ -587,6 +608,8 @@ class RtcStats(google.protobuf.message.Message): @property def certificate(self) -> global___RtcStats.Certificate: ... @property + def stream(self) -> global___RtcStats.Stream: ... + @property def track(self) -> global___RtcStats.Track: ... def __init__( self, @@ -605,15 +628,16 @@ class RtcStats(google.protobuf.message.Message): local_candidate: global___RtcStats.LocalCandidate | None = ..., remote_candidate: global___RtcStats.RemoteCandidate | None = ..., certificate: global___RtcStats.Certificate | None = ..., + stream: global___RtcStats.Stream | None = ..., track: global___RtcStats.Track | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["candidate_pair", b"candidate_pair", "certificate", b"certificate", "codec", b"codec", "data_channel", b"data_channel", "inbound_rtp", b"inbound_rtp", "local_candidate", b"local_candidate", "media_playout", b"media_playout", "media_source", b"media_source", "outbound_rtp", b"outbound_rtp", "peer_connection", b"peer_connection", "remote_candidate", b"remote_candidate", "remote_inbound_rtp", b"remote_inbound_rtp", "remote_outbound_rtp", b"remote_outbound_rtp", "stats", b"stats", "track", b"track", "transport", b"transport"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["candidate_pair", b"candidate_pair", "certificate", b"certificate", "codec", b"codec", "data_channel", b"data_channel", "inbound_rtp", b"inbound_rtp", "local_candidate", b"local_candidate", "media_playout", b"media_playout", "media_source", b"media_source", "outbound_rtp", b"outbound_rtp", "peer_connection", b"peer_connection", "remote_candidate", b"remote_candidate", "remote_inbound_rtp", b"remote_inbound_rtp", "remote_outbound_rtp", b"remote_outbound_rtp", "stats", b"stats", "track", b"track", "transport", b"transport"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["stats", b"stats"]) -> typing_extensions.Literal["codec", "inbound_rtp", "outbound_rtp", "remote_inbound_rtp", "remote_outbound_rtp", "media_source", "media_playout", "peer_connection", "data_channel", "transport", "candidate_pair", "local_candidate", "remote_candidate", "certificate", "track"] | None: ... + def HasField(self, field_name: typing.Literal["candidate_pair", b"candidate_pair", "certificate", b"certificate", "codec", b"codec", "data_channel", b"data_channel", "inbound_rtp", b"inbound_rtp", "local_candidate", b"local_candidate", "media_playout", b"media_playout", "media_source", b"media_source", "outbound_rtp", b"outbound_rtp", "peer_connection", b"peer_connection", "remote_candidate", b"remote_candidate", "remote_inbound_rtp", b"remote_inbound_rtp", "remote_outbound_rtp", b"remote_outbound_rtp", "stats", b"stats", "stream", b"stream", "track", b"track", "transport", b"transport"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["candidate_pair", b"candidate_pair", "certificate", b"certificate", "codec", b"codec", "data_channel", b"data_channel", "inbound_rtp", b"inbound_rtp", "local_candidate", b"local_candidate", "media_playout", b"media_playout", "media_source", b"media_source", "outbound_rtp", b"outbound_rtp", "peer_connection", b"peer_connection", "remote_candidate", b"remote_candidate", "remote_inbound_rtp", b"remote_inbound_rtp", "remote_outbound_rtp", b"remote_outbound_rtp", "stats", b"stats", "stream", b"stream", "track", b"track", "transport", b"transport"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["stats", b"stats"]) -> typing.Literal["codec", "inbound_rtp", "outbound_rtp", "remote_inbound_rtp", "remote_outbound_rtp", "media_source", "media_playout", "peer_connection", "data_channel", "transport", "candidate_pair", "local_candidate", "remote_candidate", "certificate", "stream", "track"] | None: ... global___RtcStats = RtcStats -@typing_extensions.final +@typing.final class RtcStatsData(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -624,14 +648,15 @@ class RtcStatsData(google.protobuf.message.Message): def __init__( self, *, - id: builtins.str = ..., - timestamp: builtins.int = ..., + id: builtins.str | None = ..., + timestamp: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["id", b"id", "timestamp", b"timestamp"]) -> None: ... + def HasField(self, field_name: typing.Literal["id", b"id", "timestamp", b"timestamp"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["id", b"id", "timestamp", b"timestamp"]) -> None: ... global___RtcStatsData = RtcStatsData -@typing_extensions.final +@typing.final class CodecStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -650,18 +675,19 @@ class CodecStats(google.protobuf.message.Message): def __init__( self, *, - payload_type: builtins.int = ..., - transport_id: builtins.str = ..., - mime_type: builtins.str = ..., - clock_rate: builtins.int = ..., - channels: builtins.int = ..., - sdp_fmtp_line: builtins.str = ..., + payload_type: builtins.int | None = ..., + transport_id: builtins.str | None = ..., + mime_type: builtins.str | None = ..., + clock_rate: builtins.int | None = ..., + channels: builtins.int | None = ..., + sdp_fmtp_line: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["channels", b"channels", "clock_rate", b"clock_rate", "mime_type", b"mime_type", "payload_type", b"payload_type", "sdp_fmtp_line", b"sdp_fmtp_line", "transport_id", b"transport_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["channels", b"channels", "clock_rate", b"clock_rate", "mime_type", b"mime_type", "payload_type", b"payload_type", "sdp_fmtp_line", b"sdp_fmtp_line", "transport_id", b"transport_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["channels", b"channels", "clock_rate", b"clock_rate", "mime_type", b"mime_type", "payload_type", b"payload_type", "sdp_fmtp_line", b"sdp_fmtp_line", "transport_id", b"transport_id"]) -> None: ... global___CodecStats = CodecStats -@typing_extensions.final +@typing.final class RtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -676,16 +702,17 @@ class RtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - ssrc: builtins.int = ..., - kind: builtins.str = ..., - transport_id: builtins.str = ..., - codec_id: builtins.str = ..., + ssrc: builtins.int | None = ..., + kind: builtins.str | None = ..., + transport_id: builtins.str | None = ..., + codec_id: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["codec_id", b"codec_id", "kind", b"kind", "ssrc", b"ssrc", "transport_id", b"transport_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["codec_id", b"codec_id", "kind", b"kind", "ssrc", b"ssrc", "transport_id", b"transport_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["codec_id", b"codec_id", "kind", b"kind", "ssrc", b"ssrc", "transport_id", b"transport_id"]) -> None: ... global___RtpStreamStats = RtpStreamStats -@typing_extensions.final +@typing.final class ReceivedRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -698,15 +725,16 @@ class ReceivedRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - packets_received: builtins.int = ..., - packets_lost: builtins.int = ..., - jitter: builtins.float = ..., + packets_received: builtins.int | None = ..., + packets_lost: builtins.int | None = ..., + jitter: builtins.float | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["jitter", b"jitter", "packets_lost", b"packets_lost", "packets_received", b"packets_received"]) -> None: ... + def HasField(self, field_name: typing.Literal["jitter", b"jitter", "packets_lost", b"packets_lost", "packets_received", b"packets_received"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["jitter", b"jitter", "packets_lost", b"packets_lost", "packets_received", b"packets_received"]) -> None: ... global___ReceivedRtpStreamStats = ReceivedRtpStreamStats -@typing_extensions.final +@typing.final class InboundRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -819,65 +847,66 @@ class InboundRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - track_identifier: builtins.str = ..., - mid: builtins.str = ..., - remote_id: builtins.str = ..., - frames_decoded: builtins.int = ..., - key_frames_decoded: builtins.int = ..., - frames_rendered: builtins.int = ..., - frames_dropped: builtins.int = ..., - frame_width: builtins.int = ..., - frame_height: builtins.int = ..., - frames_per_second: builtins.float = ..., - qp_sum: builtins.int = ..., - total_decode_time: builtins.float = ..., - total_inter_frame_delay: builtins.float = ..., - total_squared_inter_frame_delay: builtins.float = ..., - pause_count: builtins.int = ..., - total_pause_duration: builtins.float = ..., - freeze_count: builtins.int = ..., - total_freeze_duration: builtins.float = ..., - last_packet_received_timestamp: builtins.float = ..., - header_bytes_received: builtins.int = ..., - packets_discarded: builtins.int = ..., - fec_bytes_received: builtins.int = ..., - fec_packets_received: builtins.int = ..., - fec_packets_discarded: builtins.int = ..., - bytes_received: builtins.int = ..., - nack_count: builtins.int = ..., - fir_count: builtins.int = ..., - pli_count: builtins.int = ..., - total_processing_delay: builtins.float = ..., - estimated_playout_timestamp: builtins.float = ..., - jitter_buffer_delay: builtins.float = ..., - jitter_buffer_target_delay: builtins.float = ..., - jitter_buffer_emitted_count: builtins.int = ..., - jitter_buffer_minimum_delay: builtins.float = ..., - total_samples_received: builtins.int = ..., - concealed_samples: builtins.int = ..., - silent_concealed_samples: builtins.int = ..., - concealment_events: builtins.int = ..., - inserted_samples_for_deceleration: builtins.int = ..., - removed_samples_for_acceleration: builtins.int = ..., - audio_level: builtins.float = ..., - total_audio_energy: builtins.float = ..., - total_samples_duration: builtins.float = ..., - frames_received: builtins.int = ..., - decoder_implementation: builtins.str = ..., - playout_id: builtins.str = ..., - power_efficient_decoder: builtins.bool = ..., - frames_assembled_from_multiple_packets: builtins.int = ..., - total_assembly_time: builtins.float = ..., - retransmitted_packets_received: builtins.int = ..., - retransmitted_bytes_received: builtins.int = ..., - rtx_ssrc: builtins.int = ..., - fec_ssrc: builtins.int = ..., + track_identifier: builtins.str | None = ..., + mid: builtins.str | None = ..., + remote_id: builtins.str | None = ..., + frames_decoded: builtins.int | None = ..., + key_frames_decoded: builtins.int | None = ..., + frames_rendered: builtins.int | None = ..., + frames_dropped: builtins.int | None = ..., + frame_width: builtins.int | None = ..., + frame_height: builtins.int | None = ..., + frames_per_second: builtins.float | None = ..., + qp_sum: builtins.int | None = ..., + total_decode_time: builtins.float | None = ..., + total_inter_frame_delay: builtins.float | None = ..., + total_squared_inter_frame_delay: builtins.float | None = ..., + pause_count: builtins.int | None = ..., + total_pause_duration: builtins.float | None = ..., + freeze_count: builtins.int | None = ..., + total_freeze_duration: builtins.float | None = ..., + last_packet_received_timestamp: builtins.float | None = ..., + header_bytes_received: builtins.int | None = ..., + packets_discarded: builtins.int | None = ..., + fec_bytes_received: builtins.int | None = ..., + fec_packets_received: builtins.int | None = ..., + fec_packets_discarded: builtins.int | None = ..., + bytes_received: builtins.int | None = ..., + nack_count: builtins.int | None = ..., + fir_count: builtins.int | None = ..., + pli_count: builtins.int | None = ..., + total_processing_delay: builtins.float | None = ..., + estimated_playout_timestamp: builtins.float | None = ..., + jitter_buffer_delay: builtins.float | None = ..., + jitter_buffer_target_delay: builtins.float | None = ..., + jitter_buffer_emitted_count: builtins.int | None = ..., + jitter_buffer_minimum_delay: builtins.float | None = ..., + total_samples_received: builtins.int | None = ..., + concealed_samples: builtins.int | None = ..., + silent_concealed_samples: builtins.int | None = ..., + concealment_events: builtins.int | None = ..., + inserted_samples_for_deceleration: builtins.int | None = ..., + removed_samples_for_acceleration: builtins.int | None = ..., + audio_level: builtins.float | None = ..., + total_audio_energy: builtins.float | None = ..., + total_samples_duration: builtins.float | None = ..., + frames_received: builtins.int | None = ..., + decoder_implementation: builtins.str | None = ..., + playout_id: builtins.str | None = ..., + power_efficient_decoder: builtins.bool | None = ..., + frames_assembled_from_multiple_packets: builtins.int | None = ..., + total_assembly_time: builtins.float | None = ..., + retransmitted_packets_received: builtins.int | None = ..., + retransmitted_bytes_received: builtins.int | None = ..., + rtx_ssrc: builtins.int | None = ..., + fec_ssrc: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["audio_level", b"audio_level", "bytes_received", b"bytes_received", "concealed_samples", b"concealed_samples", "concealment_events", b"concealment_events", "decoder_implementation", b"decoder_implementation", "estimated_playout_timestamp", b"estimated_playout_timestamp", "fec_bytes_received", b"fec_bytes_received", "fec_packets_discarded", b"fec_packets_discarded", "fec_packets_received", b"fec_packets_received", "fec_ssrc", b"fec_ssrc", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_assembled_from_multiple_packets", b"frames_assembled_from_multiple_packets", "frames_decoded", b"frames_decoded", "frames_dropped", b"frames_dropped", "frames_per_second", b"frames_per_second", "frames_received", b"frames_received", "frames_rendered", b"frames_rendered", "freeze_count", b"freeze_count", "header_bytes_received", b"header_bytes_received", "inserted_samples_for_deceleration", b"inserted_samples_for_deceleration", "jitter_buffer_delay", b"jitter_buffer_delay", "jitter_buffer_emitted_count", b"jitter_buffer_emitted_count", "jitter_buffer_minimum_delay", b"jitter_buffer_minimum_delay", "jitter_buffer_target_delay", b"jitter_buffer_target_delay", "key_frames_decoded", b"key_frames_decoded", "last_packet_received_timestamp", b"last_packet_received_timestamp", "mid", b"mid", "nack_count", b"nack_count", "packets_discarded", b"packets_discarded", "pause_count", b"pause_count", "playout_id", b"playout_id", "pli_count", b"pli_count", "power_efficient_decoder", b"power_efficient_decoder", "qp_sum", b"qp_sum", "remote_id", b"remote_id", "removed_samples_for_acceleration", b"removed_samples_for_acceleration", "retransmitted_bytes_received", b"retransmitted_bytes_received", "retransmitted_packets_received", b"retransmitted_packets_received", "rtx_ssrc", b"rtx_ssrc", "silent_concealed_samples", b"silent_concealed_samples", "total_assembly_time", b"total_assembly_time", "total_audio_energy", b"total_audio_energy", "total_decode_time", b"total_decode_time", "total_freeze_duration", b"total_freeze_duration", "total_inter_frame_delay", b"total_inter_frame_delay", "total_pause_duration", b"total_pause_duration", "total_processing_delay", b"total_processing_delay", "total_samples_duration", b"total_samples_duration", "total_samples_received", b"total_samples_received", "total_squared_inter_frame_delay", b"total_squared_inter_frame_delay", "track_identifier", b"track_identifier"]) -> None: ... + def HasField(self, field_name: typing.Literal["audio_level", b"audio_level", "bytes_received", b"bytes_received", "concealed_samples", b"concealed_samples", "concealment_events", b"concealment_events", "decoder_implementation", b"decoder_implementation", "estimated_playout_timestamp", b"estimated_playout_timestamp", "fec_bytes_received", b"fec_bytes_received", "fec_packets_discarded", b"fec_packets_discarded", "fec_packets_received", b"fec_packets_received", "fec_ssrc", b"fec_ssrc", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_assembled_from_multiple_packets", b"frames_assembled_from_multiple_packets", "frames_decoded", b"frames_decoded", "frames_dropped", b"frames_dropped", "frames_per_second", b"frames_per_second", "frames_received", b"frames_received", "frames_rendered", b"frames_rendered", "freeze_count", b"freeze_count", "header_bytes_received", b"header_bytes_received", "inserted_samples_for_deceleration", b"inserted_samples_for_deceleration", "jitter_buffer_delay", b"jitter_buffer_delay", "jitter_buffer_emitted_count", b"jitter_buffer_emitted_count", "jitter_buffer_minimum_delay", b"jitter_buffer_minimum_delay", "jitter_buffer_target_delay", b"jitter_buffer_target_delay", "key_frames_decoded", b"key_frames_decoded", "last_packet_received_timestamp", b"last_packet_received_timestamp", "mid", b"mid", "nack_count", b"nack_count", "packets_discarded", b"packets_discarded", "pause_count", b"pause_count", "playout_id", b"playout_id", "pli_count", b"pli_count", "power_efficient_decoder", b"power_efficient_decoder", "qp_sum", b"qp_sum", "remote_id", b"remote_id", "removed_samples_for_acceleration", b"removed_samples_for_acceleration", "retransmitted_bytes_received", b"retransmitted_bytes_received", "retransmitted_packets_received", b"retransmitted_packets_received", "rtx_ssrc", b"rtx_ssrc", "silent_concealed_samples", b"silent_concealed_samples", "total_assembly_time", b"total_assembly_time", "total_audio_energy", b"total_audio_energy", "total_decode_time", b"total_decode_time", "total_freeze_duration", b"total_freeze_duration", "total_inter_frame_delay", b"total_inter_frame_delay", "total_pause_duration", b"total_pause_duration", "total_processing_delay", b"total_processing_delay", "total_samples_duration", b"total_samples_duration", "total_samples_received", b"total_samples_received", "total_squared_inter_frame_delay", b"total_squared_inter_frame_delay", "track_identifier", b"track_identifier"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_level", b"audio_level", "bytes_received", b"bytes_received", "concealed_samples", b"concealed_samples", "concealment_events", b"concealment_events", "decoder_implementation", b"decoder_implementation", "estimated_playout_timestamp", b"estimated_playout_timestamp", "fec_bytes_received", b"fec_bytes_received", "fec_packets_discarded", b"fec_packets_discarded", "fec_packets_received", b"fec_packets_received", "fec_ssrc", b"fec_ssrc", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_assembled_from_multiple_packets", b"frames_assembled_from_multiple_packets", "frames_decoded", b"frames_decoded", "frames_dropped", b"frames_dropped", "frames_per_second", b"frames_per_second", "frames_received", b"frames_received", "frames_rendered", b"frames_rendered", "freeze_count", b"freeze_count", "header_bytes_received", b"header_bytes_received", "inserted_samples_for_deceleration", b"inserted_samples_for_deceleration", "jitter_buffer_delay", b"jitter_buffer_delay", "jitter_buffer_emitted_count", b"jitter_buffer_emitted_count", "jitter_buffer_minimum_delay", b"jitter_buffer_minimum_delay", "jitter_buffer_target_delay", b"jitter_buffer_target_delay", "key_frames_decoded", b"key_frames_decoded", "last_packet_received_timestamp", b"last_packet_received_timestamp", "mid", b"mid", "nack_count", b"nack_count", "packets_discarded", b"packets_discarded", "pause_count", b"pause_count", "playout_id", b"playout_id", "pli_count", b"pli_count", "power_efficient_decoder", b"power_efficient_decoder", "qp_sum", b"qp_sum", "remote_id", b"remote_id", "removed_samples_for_acceleration", b"removed_samples_for_acceleration", "retransmitted_bytes_received", b"retransmitted_bytes_received", "retransmitted_packets_received", b"retransmitted_packets_received", "rtx_ssrc", b"rtx_ssrc", "silent_concealed_samples", b"silent_concealed_samples", "total_assembly_time", b"total_assembly_time", "total_audio_energy", b"total_audio_energy", "total_decode_time", b"total_decode_time", "total_freeze_duration", b"total_freeze_duration", "total_inter_frame_delay", b"total_inter_frame_delay", "total_pause_duration", b"total_pause_duration", "total_processing_delay", b"total_processing_delay", "total_samples_duration", b"total_samples_duration", "total_samples_received", b"total_samples_received", "total_squared_inter_frame_delay", b"total_squared_inter_frame_delay", "track_identifier", b"track_identifier"]) -> None: ... global___InboundRtpStreamStats = InboundRtpStreamStats -@typing_extensions.final +@typing.final class SentRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -888,18 +917,19 @@ class SentRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - packets_sent: builtins.int = ..., - bytes_sent: builtins.int = ..., + packets_sent: builtins.int | None = ..., + bytes_sent: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["bytes_sent", b"bytes_sent", "packets_sent", b"packets_sent"]) -> None: ... + def HasField(self, field_name: typing.Literal["bytes_sent", b"bytes_sent", "packets_sent", b"packets_sent"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bytes_sent", b"bytes_sent", "packets_sent", b"packets_sent"]) -> None: ... global___SentRtpStreamStats = SentRtpStreamStats -@typing_extensions.final +@typing.final class OutboundRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class QualityLimitationDurationsEntry(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -910,10 +940,11 @@ class OutboundRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - key: builtins.str = ..., - value: builtins.float = ..., + key: builtins.str | None = ..., + value: builtins.float | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["key", b"key", "value", b"value"]) -> None: ... + def HasField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["key", b"key", "value", b"value"]) -> None: ... MID_FIELD_NUMBER: builtins.int MEDIA_SOURCE_ID_FIELD_NUMBER: builtins.int @@ -944,7 +975,7 @@ class OutboundRtpStreamStats(google.protobuf.message.Message): ENCODER_IMPLEMENTATION_FIELD_NUMBER: builtins.int POWER_EFFICIENT_ENCODER_FIELD_NUMBER: builtins.int ACTIVE_FIELD_NUMBER: builtins.int - SCALIBILITY_MODE_FIELD_NUMBER: builtins.int + SCALABILITY_MODE_FIELD_NUMBER: builtins.int mid: builtins.str media_source_id: builtins.str remote_id: builtins.str @@ -966,8 +997,6 @@ class OutboundRtpStreamStats(google.protobuf.message.Message): total_encode_time: builtins.float total_packet_send_delay: builtins.float quality_limitation_reason: global___QualityLimitationReason.ValueType - @property - def quality_limitation_durations(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.float]: ... quality_limitation_resolution_changes: builtins.int nack_count: builtins.int fir_count: builtins.int @@ -975,46 +1004,49 @@ class OutboundRtpStreamStats(google.protobuf.message.Message): encoder_implementation: builtins.str power_efficient_encoder: builtins.bool active: builtins.bool - scalibility_mode: builtins.str + scalability_mode: builtins.str + @property + def quality_limitation_durations(self) -> google.protobuf.internal.containers.ScalarMap[builtins.str, builtins.float]: ... def __init__( self, *, - mid: builtins.str = ..., - media_source_id: builtins.str = ..., - remote_id: builtins.str = ..., - rid: builtins.str = ..., - header_bytes_sent: builtins.int = ..., - retransmitted_packets_sent: builtins.int = ..., - retransmitted_bytes_sent: builtins.int = ..., - rtx_ssrc: builtins.int = ..., - target_bitrate: builtins.float = ..., - total_encoded_bytes_target: builtins.int = ..., - frame_width: builtins.int = ..., - frame_height: builtins.int = ..., - frames_per_second: builtins.float = ..., - frames_sent: builtins.int = ..., - huge_frames_sent: builtins.int = ..., - frames_encoded: builtins.int = ..., - key_frames_encoded: builtins.int = ..., - qp_sum: builtins.int = ..., - total_encode_time: builtins.float = ..., - total_packet_send_delay: builtins.float = ..., - quality_limitation_reason: global___QualityLimitationReason.ValueType = ..., + mid: builtins.str | None = ..., + media_source_id: builtins.str | None = ..., + remote_id: builtins.str | None = ..., + rid: builtins.str | None = ..., + header_bytes_sent: builtins.int | None = ..., + retransmitted_packets_sent: builtins.int | None = ..., + retransmitted_bytes_sent: builtins.int | None = ..., + rtx_ssrc: builtins.int | None = ..., + target_bitrate: builtins.float | None = ..., + total_encoded_bytes_target: builtins.int | None = ..., + frame_width: builtins.int | None = ..., + frame_height: builtins.int | None = ..., + frames_per_second: builtins.float | None = ..., + frames_sent: builtins.int | None = ..., + huge_frames_sent: builtins.int | None = ..., + frames_encoded: builtins.int | None = ..., + key_frames_encoded: builtins.int | None = ..., + qp_sum: builtins.int | None = ..., + total_encode_time: builtins.float | None = ..., + total_packet_send_delay: builtins.float | None = ..., + quality_limitation_reason: global___QualityLimitationReason.ValueType | None = ..., quality_limitation_durations: collections.abc.Mapping[builtins.str, builtins.float] | None = ..., - quality_limitation_resolution_changes: builtins.int = ..., - nack_count: builtins.int = ..., - fir_count: builtins.int = ..., - pli_count: builtins.int = ..., - encoder_implementation: builtins.str = ..., - power_efficient_encoder: builtins.bool = ..., - active: builtins.bool = ..., - scalibility_mode: builtins.str = ..., + quality_limitation_resolution_changes: builtins.int | None = ..., + nack_count: builtins.int | None = ..., + fir_count: builtins.int | None = ..., + pli_count: builtins.int | None = ..., + encoder_implementation: builtins.str | None = ..., + power_efficient_encoder: builtins.bool | None = ..., + active: builtins.bool | None = ..., + scalability_mode: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["active", b"active", "encoder_implementation", b"encoder_implementation", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_encoded", b"frames_encoded", "frames_per_second", b"frames_per_second", "frames_sent", b"frames_sent", "header_bytes_sent", b"header_bytes_sent", "huge_frames_sent", b"huge_frames_sent", "key_frames_encoded", b"key_frames_encoded", "media_source_id", b"media_source_id", "mid", b"mid", "nack_count", b"nack_count", "pli_count", b"pli_count", "power_efficient_encoder", b"power_efficient_encoder", "qp_sum", b"qp_sum", "quality_limitation_durations", b"quality_limitation_durations", "quality_limitation_reason", b"quality_limitation_reason", "quality_limitation_resolution_changes", b"quality_limitation_resolution_changes", "remote_id", b"remote_id", "retransmitted_bytes_sent", b"retransmitted_bytes_sent", "retransmitted_packets_sent", b"retransmitted_packets_sent", "rid", b"rid", "rtx_ssrc", b"rtx_ssrc", "scalibility_mode", b"scalibility_mode", "target_bitrate", b"target_bitrate", "total_encode_time", b"total_encode_time", "total_encoded_bytes_target", b"total_encoded_bytes_target", "total_packet_send_delay", b"total_packet_send_delay"]) -> None: ... + def HasField(self, field_name: typing.Literal["active", b"active", "encoder_implementation", b"encoder_implementation", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_encoded", b"frames_encoded", "frames_per_second", b"frames_per_second", "frames_sent", b"frames_sent", "header_bytes_sent", b"header_bytes_sent", "huge_frames_sent", b"huge_frames_sent", "key_frames_encoded", b"key_frames_encoded", "media_source_id", b"media_source_id", "mid", b"mid", "nack_count", b"nack_count", "pli_count", b"pli_count", "power_efficient_encoder", b"power_efficient_encoder", "qp_sum", b"qp_sum", "quality_limitation_reason", b"quality_limitation_reason", "quality_limitation_resolution_changes", b"quality_limitation_resolution_changes", "remote_id", b"remote_id", "retransmitted_bytes_sent", b"retransmitted_bytes_sent", "retransmitted_packets_sent", b"retransmitted_packets_sent", "rid", b"rid", "rtx_ssrc", b"rtx_ssrc", "scalability_mode", b"scalability_mode", "target_bitrate", b"target_bitrate", "total_encode_time", b"total_encode_time", "total_encoded_bytes_target", b"total_encoded_bytes_target", "total_packet_send_delay", b"total_packet_send_delay"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["active", b"active", "encoder_implementation", b"encoder_implementation", "fir_count", b"fir_count", "frame_height", b"frame_height", "frame_width", b"frame_width", "frames_encoded", b"frames_encoded", "frames_per_second", b"frames_per_second", "frames_sent", b"frames_sent", "header_bytes_sent", b"header_bytes_sent", "huge_frames_sent", b"huge_frames_sent", "key_frames_encoded", b"key_frames_encoded", "media_source_id", b"media_source_id", "mid", b"mid", "nack_count", b"nack_count", "pli_count", b"pli_count", "power_efficient_encoder", b"power_efficient_encoder", "qp_sum", b"qp_sum", "quality_limitation_durations", b"quality_limitation_durations", "quality_limitation_reason", b"quality_limitation_reason", "quality_limitation_resolution_changes", b"quality_limitation_resolution_changes", "remote_id", b"remote_id", "retransmitted_bytes_sent", b"retransmitted_bytes_sent", "retransmitted_packets_sent", b"retransmitted_packets_sent", "rid", b"rid", "rtx_ssrc", b"rtx_ssrc", "scalability_mode", b"scalability_mode", "target_bitrate", b"target_bitrate", "total_encode_time", b"total_encode_time", "total_encoded_bytes_target", b"total_encoded_bytes_target", "total_packet_send_delay", b"total_packet_send_delay"]) -> None: ... global___OutboundRtpStreamStats = OutboundRtpStreamStats -@typing_extensions.final +@typing.final class RemoteInboundRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1031,17 +1063,18 @@ class RemoteInboundRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - local_id: builtins.str = ..., - round_trip_time: builtins.float = ..., - total_round_trip_time: builtins.float = ..., - fraction_lost: builtins.float = ..., - round_trip_time_measurements: builtins.int = ..., + local_id: builtins.str | None = ..., + round_trip_time: builtins.float | None = ..., + total_round_trip_time: builtins.float | None = ..., + fraction_lost: builtins.float | None = ..., + round_trip_time_measurements: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["fraction_lost", b"fraction_lost", "local_id", b"local_id", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> None: ... + def HasField(self, field_name: typing.Literal["fraction_lost", b"fraction_lost", "local_id", b"local_id", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["fraction_lost", b"fraction_lost", "local_id", b"local_id", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> None: ... global___RemoteInboundRtpStreamStats = RemoteInboundRtpStreamStats -@typing_extensions.final +@typing.final class RemoteOutboundRtpStreamStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1060,18 +1093,19 @@ class RemoteOutboundRtpStreamStats(google.protobuf.message.Message): def __init__( self, *, - local_id: builtins.str = ..., - remote_timestamp: builtins.float = ..., - reports_sent: builtins.int = ..., - round_trip_time: builtins.float = ..., - total_round_trip_time: builtins.float = ..., - round_trip_time_measurements: builtins.int = ..., + local_id: builtins.str | None = ..., + remote_timestamp: builtins.float | None = ..., + reports_sent: builtins.int | None = ..., + round_trip_time: builtins.float | None = ..., + total_round_trip_time: builtins.float | None = ..., + round_trip_time_measurements: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["local_id", b"local_id", "remote_timestamp", b"remote_timestamp", "reports_sent", b"reports_sent", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> None: ... + def HasField(self, field_name: typing.Literal["local_id", b"local_id", "remote_timestamp", b"remote_timestamp", "reports_sent", b"reports_sent", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["local_id", b"local_id", "remote_timestamp", b"remote_timestamp", "reports_sent", b"reports_sent", "round_trip_time", b"round_trip_time", "round_trip_time_measurements", b"round_trip_time_measurements", "total_round_trip_time", b"total_round_trip_time"]) -> None: ... global___RemoteOutboundRtpStreamStats = RemoteOutboundRtpStreamStats -@typing_extensions.final +@typing.final class MediaSourceStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1082,14 +1116,15 @@ class MediaSourceStats(google.protobuf.message.Message): def __init__( self, *, - track_identifier: builtins.str = ..., - kind: builtins.str = ..., + track_identifier: builtins.str | None = ..., + kind: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["kind", b"kind", "track_identifier", b"track_identifier"]) -> None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "track_identifier", b"track_identifier"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "track_identifier", b"track_identifier"]) -> None: ... global___MediaSourceStats = MediaSourceStats -@typing_extensions.final +@typing.final class AudioSourceStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1114,21 +1149,22 @@ class AudioSourceStats(google.protobuf.message.Message): def __init__( self, *, - audio_level: builtins.float = ..., - total_audio_energy: builtins.float = ..., - total_samples_duration: builtins.float = ..., - echo_return_loss: builtins.float = ..., - echo_return_loss_enhancement: builtins.float = ..., - dropped_samples_duration: builtins.float = ..., - dropped_samples_events: builtins.int = ..., - total_capture_delay: builtins.float = ..., - total_samples_captured: builtins.int = ..., + audio_level: builtins.float | None = ..., + total_audio_energy: builtins.float | None = ..., + total_samples_duration: builtins.float | None = ..., + echo_return_loss: builtins.float | None = ..., + echo_return_loss_enhancement: builtins.float | None = ..., + dropped_samples_duration: builtins.float | None = ..., + dropped_samples_events: builtins.int | None = ..., + total_capture_delay: builtins.float | None = ..., + total_samples_captured: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["audio_level", b"audio_level", "dropped_samples_duration", b"dropped_samples_duration", "dropped_samples_events", b"dropped_samples_events", "echo_return_loss", b"echo_return_loss", "echo_return_loss_enhancement", b"echo_return_loss_enhancement", "total_audio_energy", b"total_audio_energy", "total_capture_delay", b"total_capture_delay", "total_samples_captured", b"total_samples_captured", "total_samples_duration", b"total_samples_duration"]) -> None: ... + def HasField(self, field_name: typing.Literal["audio_level", b"audio_level", "dropped_samples_duration", b"dropped_samples_duration", "dropped_samples_events", b"dropped_samples_events", "echo_return_loss", b"echo_return_loss", "echo_return_loss_enhancement", b"echo_return_loss_enhancement", "total_audio_energy", b"total_audio_energy", "total_capture_delay", b"total_capture_delay", "total_samples_captured", b"total_samples_captured", "total_samples_duration", b"total_samples_duration"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_level", b"audio_level", "dropped_samples_duration", b"dropped_samples_duration", "dropped_samples_events", b"dropped_samples_events", "echo_return_loss", b"echo_return_loss", "echo_return_loss_enhancement", b"echo_return_loss_enhancement", "total_audio_energy", b"total_audio_energy", "total_capture_delay", b"total_capture_delay", "total_samples_captured", b"total_samples_captured", "total_samples_duration", b"total_samples_duration"]) -> None: ... global___AudioSourceStats = AudioSourceStats -@typing_extensions.final +@typing.final class VideoSourceStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1143,16 +1179,17 @@ class VideoSourceStats(google.protobuf.message.Message): def __init__( self, *, - width: builtins.int = ..., - height: builtins.int = ..., - frames: builtins.int = ..., - frames_per_second: builtins.float = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., + frames: builtins.int | None = ..., + frames_per_second: builtins.float | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["frames", b"frames", "frames_per_second", b"frames_per_second", "height", b"height", "width", b"width"]) -> None: ... + def HasField(self, field_name: typing.Literal["frames", b"frames", "frames_per_second", b"frames_per_second", "height", b"height", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frames", b"frames", "frames_per_second", b"frames_per_second", "height", b"height", "width", b"width"]) -> None: ... global___VideoSourceStats = VideoSourceStats -@typing_extensions.final +@typing.final class AudioPlayoutStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1171,18 +1208,19 @@ class AudioPlayoutStats(google.protobuf.message.Message): def __init__( self, *, - kind: builtins.str = ..., - synthesized_samples_duration: builtins.float = ..., - synthesized_samples_events: builtins.int = ..., - total_samples_duration: builtins.float = ..., - total_playout_delay: builtins.float = ..., - total_samples_count: builtins.int = ..., + kind: builtins.str | None = ..., + synthesized_samples_duration: builtins.float | None = ..., + synthesized_samples_events: builtins.int | None = ..., + total_samples_duration: builtins.float | None = ..., + total_playout_delay: builtins.float | None = ..., + total_samples_count: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["kind", b"kind", "synthesized_samples_duration", b"synthesized_samples_duration", "synthesized_samples_events", b"synthesized_samples_events", "total_playout_delay", b"total_playout_delay", "total_samples_count", b"total_samples_count", "total_samples_duration", b"total_samples_duration"]) -> None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "synthesized_samples_duration", b"synthesized_samples_duration", "synthesized_samples_events", b"synthesized_samples_events", "total_playout_delay", b"total_playout_delay", "total_samples_count", b"total_samples_count", "total_samples_duration", b"total_samples_duration"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "synthesized_samples_duration", b"synthesized_samples_duration", "synthesized_samples_events", b"synthesized_samples_events", "total_playout_delay", b"total_playout_delay", "total_samples_count", b"total_samples_count", "total_samples_duration", b"total_samples_duration"]) -> None: ... global___AudioPlayoutStats = AudioPlayoutStats -@typing_extensions.final +@typing.final class PeerConnectionStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1193,14 +1231,15 @@ class PeerConnectionStats(google.protobuf.message.Message): def __init__( self, *, - data_channels_opened: builtins.int = ..., - data_channels_closed: builtins.int = ..., + data_channels_opened: builtins.int | None = ..., + data_channels_closed: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["data_channels_closed", b"data_channels_closed", "data_channels_opened", b"data_channels_opened"]) -> None: ... + def HasField(self, field_name: typing.Literal["data_channels_closed", b"data_channels_closed", "data_channels_opened", b"data_channels_opened"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_channels_closed", b"data_channels_closed", "data_channels_opened", b"data_channels_opened"]) -> None: ... global___PeerConnectionStats = PeerConnectionStats -@typing_extensions.final +@typing.final class DataChannelStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1223,22 +1262,21 @@ class DataChannelStats(google.protobuf.message.Message): def __init__( self, *, - label: builtins.str = ..., - protocol: builtins.str = ..., - data_channel_identifier: builtins.int = ..., + label: builtins.str | None = ..., + protocol: builtins.str | None = ..., + data_channel_identifier: builtins.int | None = ..., state: global___DataChannelState.ValueType | None = ..., - messages_sent: builtins.int = ..., - bytes_sent: builtins.int = ..., - messages_received: builtins.int = ..., - bytes_received: builtins.int = ..., + messages_sent: builtins.int | None = ..., + bytes_sent: builtins.int | None = ..., + messages_received: builtins.int | None = ..., + bytes_received: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_state", b"_state", "state", b"state"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_state", b"_state", "bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "data_channel_identifier", b"data_channel_identifier", "label", b"label", "messages_received", b"messages_received", "messages_sent", b"messages_sent", "protocol", b"protocol", "state", b"state"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_state", b"_state"]) -> typing_extensions.Literal["state"] | None: ... + def HasField(self, field_name: typing.Literal["bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "data_channel_identifier", b"data_channel_identifier", "label", b"label", "messages_received", b"messages_received", "messages_sent", b"messages_sent", "protocol", b"protocol", "state", b"state"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "data_channel_identifier", b"data_channel_identifier", "label", b"label", "messages_received", b"messages_received", "messages_sent", b"messages_sent", "protocol", b"protocol", "state", b"state"]) -> None: ... global___DataChannelStats = DataChannelStats -@typing_extensions.final +@typing.final class TransportStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1277,33 +1315,29 @@ class TransportStats(google.protobuf.message.Message): def __init__( self, *, - packets_sent: builtins.int = ..., - packets_received: builtins.int = ..., - bytes_sent: builtins.int = ..., - bytes_received: builtins.int = ..., - ice_role: global___IceRole.ValueType = ..., - ice_local_username_fragment: builtins.str = ..., + packets_sent: builtins.int | None = ..., + packets_received: builtins.int | None = ..., + bytes_sent: builtins.int | None = ..., + bytes_received: builtins.int | None = ..., + ice_role: global___IceRole.ValueType | None = ..., + ice_local_username_fragment: builtins.str | None = ..., dtls_state: global___DtlsTransportState.ValueType | None = ..., ice_state: global___IceTransportState.ValueType | None = ..., - selected_candidate_pair_id: builtins.str = ..., - local_certificate_id: builtins.str = ..., - remote_certificate_id: builtins.str = ..., - tls_version: builtins.str = ..., - dtls_cipher: builtins.str = ..., - dtls_role: global___DtlsRole.ValueType = ..., - srtp_cipher: builtins.str = ..., - selected_candidate_pair_changes: builtins.int = ..., + selected_candidate_pair_id: builtins.str | None = ..., + local_certificate_id: builtins.str | None = ..., + remote_certificate_id: builtins.str | None = ..., + tls_version: builtins.str | None = ..., + dtls_cipher: builtins.str | None = ..., + dtls_role: global___DtlsRole.ValueType | None = ..., + srtp_cipher: builtins.str | None = ..., + selected_candidate_pair_changes: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_dtls_state", b"_dtls_state", "_ice_state", b"_ice_state", "dtls_state", b"dtls_state", "ice_state", b"ice_state"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_dtls_state", b"_dtls_state", "_ice_state", b"_ice_state", "bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "dtls_cipher", b"dtls_cipher", "dtls_role", b"dtls_role", "dtls_state", b"dtls_state", "ice_local_username_fragment", b"ice_local_username_fragment", "ice_role", b"ice_role", "ice_state", b"ice_state", "local_certificate_id", b"local_certificate_id", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_certificate_id", b"remote_certificate_id", "selected_candidate_pair_changes", b"selected_candidate_pair_changes", "selected_candidate_pair_id", b"selected_candidate_pair_id", "srtp_cipher", b"srtp_cipher", "tls_version", b"tls_version"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_dtls_state", b"_dtls_state"]) -> typing_extensions.Literal["dtls_state"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_ice_state", b"_ice_state"]) -> typing_extensions.Literal["ice_state"] | None: ... + def HasField(self, field_name: typing.Literal["bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "dtls_cipher", b"dtls_cipher", "dtls_role", b"dtls_role", "dtls_state", b"dtls_state", "ice_local_username_fragment", b"ice_local_username_fragment", "ice_role", b"ice_role", "ice_state", b"ice_state", "local_certificate_id", b"local_certificate_id", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_certificate_id", b"remote_certificate_id", "selected_candidate_pair_changes", b"selected_candidate_pair_changes", "selected_candidate_pair_id", b"selected_candidate_pair_id", "srtp_cipher", b"srtp_cipher", "tls_version", b"tls_version"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "dtls_cipher", b"dtls_cipher", "dtls_role", b"dtls_role", "dtls_state", b"dtls_state", "ice_local_username_fragment", b"ice_local_username_fragment", "ice_role", b"ice_role", "ice_state", b"ice_state", "local_certificate_id", b"local_certificate_id", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_certificate_id", b"remote_certificate_id", "selected_candidate_pair_changes", b"selected_candidate_pair_changes", "selected_candidate_pair_id", b"selected_candidate_pair_id", "srtp_cipher", b"srtp_cipher", "tls_version", b"tls_version"]) -> None: ... global___TransportStats = TransportStats -@typing_extensions.final +@typing.final class CandidatePairStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1354,36 +1388,35 @@ class CandidatePairStats(google.protobuf.message.Message): def __init__( self, *, - transport_id: builtins.str = ..., - local_candidate_id: builtins.str = ..., - remote_candidate_id: builtins.str = ..., + transport_id: builtins.str | None = ..., + local_candidate_id: builtins.str | None = ..., + remote_candidate_id: builtins.str | None = ..., state: global___IceCandidatePairState.ValueType | None = ..., - nominated: builtins.bool = ..., - packets_sent: builtins.int = ..., - packets_received: builtins.int = ..., - bytes_sent: builtins.int = ..., - bytes_received: builtins.int = ..., - last_packet_sent_timestamp: builtins.float = ..., - last_packet_received_timestamp: builtins.float = ..., - total_round_trip_time: builtins.float = ..., - current_round_trip_time: builtins.float = ..., - available_outgoing_bitrate: builtins.float = ..., - available_incoming_bitrate: builtins.float = ..., - requests_received: builtins.int = ..., - requests_sent: builtins.int = ..., - responses_received: builtins.int = ..., - responses_sent: builtins.int = ..., - consent_requests_sent: builtins.int = ..., - packets_discarded_on_send: builtins.int = ..., - bytes_discarded_on_send: builtins.int = ..., + nominated: builtins.bool | None = ..., + packets_sent: builtins.int | None = ..., + packets_received: builtins.int | None = ..., + bytes_sent: builtins.int | None = ..., + bytes_received: builtins.int | None = ..., + last_packet_sent_timestamp: builtins.float | None = ..., + last_packet_received_timestamp: builtins.float | None = ..., + total_round_trip_time: builtins.float | None = ..., + current_round_trip_time: builtins.float | None = ..., + available_outgoing_bitrate: builtins.float | None = ..., + available_incoming_bitrate: builtins.float | None = ..., + requests_received: builtins.int | None = ..., + requests_sent: builtins.int | None = ..., + responses_received: builtins.int | None = ..., + responses_sent: builtins.int | None = ..., + consent_requests_sent: builtins.int | None = ..., + packets_discarded_on_send: builtins.int | None = ..., + bytes_discarded_on_send: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_state", b"_state", "state", b"state"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_state", b"_state", "available_incoming_bitrate", b"available_incoming_bitrate", "available_outgoing_bitrate", b"available_outgoing_bitrate", "bytes_discarded_on_send", b"bytes_discarded_on_send", "bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "consent_requests_sent", b"consent_requests_sent", "current_round_trip_time", b"current_round_trip_time", "last_packet_received_timestamp", b"last_packet_received_timestamp", "last_packet_sent_timestamp", b"last_packet_sent_timestamp", "local_candidate_id", b"local_candidate_id", "nominated", b"nominated", "packets_discarded_on_send", b"packets_discarded_on_send", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_candidate_id", b"remote_candidate_id", "requests_received", b"requests_received", "requests_sent", b"requests_sent", "responses_received", b"responses_received", "responses_sent", b"responses_sent", "state", b"state", "total_round_trip_time", b"total_round_trip_time", "transport_id", b"transport_id"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_state", b"_state"]) -> typing_extensions.Literal["state"] | None: ... + def HasField(self, field_name: typing.Literal["available_incoming_bitrate", b"available_incoming_bitrate", "available_outgoing_bitrate", b"available_outgoing_bitrate", "bytes_discarded_on_send", b"bytes_discarded_on_send", "bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "consent_requests_sent", b"consent_requests_sent", "current_round_trip_time", b"current_round_trip_time", "last_packet_received_timestamp", b"last_packet_received_timestamp", "last_packet_sent_timestamp", b"last_packet_sent_timestamp", "local_candidate_id", b"local_candidate_id", "nominated", b"nominated", "packets_discarded_on_send", b"packets_discarded_on_send", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_candidate_id", b"remote_candidate_id", "requests_received", b"requests_received", "requests_sent", b"requests_sent", "responses_received", b"responses_received", "responses_sent", b"responses_sent", "state", b"state", "total_round_trip_time", b"total_round_trip_time", "transport_id", b"transport_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["available_incoming_bitrate", b"available_incoming_bitrate", "available_outgoing_bitrate", b"available_outgoing_bitrate", "bytes_discarded_on_send", b"bytes_discarded_on_send", "bytes_received", b"bytes_received", "bytes_sent", b"bytes_sent", "consent_requests_sent", b"consent_requests_sent", "current_round_trip_time", b"current_round_trip_time", "last_packet_received_timestamp", b"last_packet_received_timestamp", "last_packet_sent_timestamp", b"last_packet_sent_timestamp", "local_candidate_id", b"local_candidate_id", "nominated", b"nominated", "packets_discarded_on_send", b"packets_discarded_on_send", "packets_received", b"packets_received", "packets_sent", b"packets_sent", "remote_candidate_id", b"remote_candidate_id", "requests_received", b"requests_received", "requests_sent", b"requests_sent", "responses_received", b"responses_received", "responses_sent", b"responses_sent", "state", b"state", "total_round_trip_time", b"total_round_trip_time", "transport_id", b"transport_id"]) -> None: ... global___CandidatePairStats = CandidatePairStats -@typing_extensions.final +@typing.final class IceCandidateStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1416,32 +1449,26 @@ class IceCandidateStats(google.protobuf.message.Message): def __init__( self, *, - transport_id: builtins.str = ..., - address: builtins.str = ..., - port: builtins.int = ..., - protocol: builtins.str = ..., + transport_id: builtins.str | None = ..., + address: builtins.str | None = ..., + port: builtins.int | None = ..., + protocol: builtins.str | None = ..., candidate_type: global___IceCandidateType.ValueType | None = ..., - priority: builtins.int = ..., - url: builtins.str = ..., + priority: builtins.int | None = ..., + url: builtins.str | None = ..., relay_protocol: global___IceServerTransportProtocol.ValueType | None = ..., - foundation: builtins.str = ..., - related_address: builtins.str = ..., - related_port: builtins.int = ..., - username_fragment: builtins.str = ..., + foundation: builtins.str | None = ..., + related_address: builtins.str | None = ..., + related_port: builtins.int | None = ..., + username_fragment: builtins.str | None = ..., tcp_type: global___IceTcpCandidateType.ValueType | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_candidate_type", b"_candidate_type", "_relay_protocol", b"_relay_protocol", "_tcp_type", b"_tcp_type", "candidate_type", b"candidate_type", "relay_protocol", b"relay_protocol", "tcp_type", b"tcp_type"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_candidate_type", b"_candidate_type", "_relay_protocol", b"_relay_protocol", "_tcp_type", b"_tcp_type", "address", b"address", "candidate_type", b"candidate_type", "foundation", b"foundation", "port", b"port", "priority", b"priority", "protocol", b"protocol", "related_address", b"related_address", "related_port", b"related_port", "relay_protocol", b"relay_protocol", "tcp_type", b"tcp_type", "transport_id", b"transport_id", "url", b"url", "username_fragment", b"username_fragment"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_candidate_type", b"_candidate_type"]) -> typing_extensions.Literal["candidate_type"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_relay_protocol", b"_relay_protocol"]) -> typing_extensions.Literal["relay_protocol"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing_extensions.Literal["_tcp_type", b"_tcp_type"]) -> typing_extensions.Literal["tcp_type"] | None: ... + def HasField(self, field_name: typing.Literal["address", b"address", "candidate_type", b"candidate_type", "foundation", b"foundation", "port", b"port", "priority", b"priority", "protocol", b"protocol", "related_address", b"related_address", "related_port", b"related_port", "relay_protocol", b"relay_protocol", "tcp_type", b"tcp_type", "transport_id", b"transport_id", "url", b"url", "username_fragment", b"username_fragment"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["address", b"address", "candidate_type", b"candidate_type", "foundation", b"foundation", "port", b"port", "priority", b"priority", "protocol", b"protocol", "related_address", b"related_address", "related_port", b"related_port", "relay_protocol", b"relay_protocol", "tcp_type", b"tcp_type", "transport_id", b"transport_id", "url", b"url", "username_fragment", b"username_fragment"]) -> None: ... global___IceCandidateStats = IceCandidateStats -@typing_extensions.final +@typing.final class CertificateStats(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -1456,11 +1483,32 @@ class CertificateStats(google.protobuf.message.Message): def __init__( self, *, - fingerprint: builtins.str = ..., - fingerprint_algorithm: builtins.str = ..., - base64_certificate: builtins.str = ..., - issuer_certificate_id: builtins.str = ..., + fingerprint: builtins.str | None = ..., + fingerprint_algorithm: builtins.str | None = ..., + base64_certificate: builtins.str | None = ..., + issuer_certificate_id: builtins.str | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["base64_certificate", b"base64_certificate", "fingerprint", b"fingerprint", "fingerprint_algorithm", b"fingerprint_algorithm", "issuer_certificate_id", b"issuer_certificate_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["base64_certificate", b"base64_certificate", "fingerprint", b"fingerprint", "fingerprint_algorithm", b"fingerprint_algorithm", "issuer_certificate_id", b"issuer_certificate_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["base64_certificate", b"base64_certificate", "fingerprint", b"fingerprint", "fingerprint_algorithm", b"fingerprint_algorithm", "issuer_certificate_id", b"issuer_certificate_id"]) -> None: ... global___CertificateStats = CertificateStats + +@typing.final +class StreamStats(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ID_FIELD_NUMBER: builtins.int + STREAM_IDENTIFIER_FIELD_NUMBER: builtins.int + id: builtins.str + stream_identifier: builtins.str + """required int64 timestamp = 3;""" + def __init__( + self, + *, + id: builtins.str | None = ..., + stream_identifier: builtins.str | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["id", b"id", "stream_identifier", b"stream_identifier"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["id", b"id", "stream_identifier", b"stream_identifier"]) -> None: ... + +global___StreamStats = StreamStats diff --git a/livekit-rtc/livekit/rtc/_proto/track_pb2.py b/livekit-rtc/livekit/rtc/_proto/track_pb2.py index 5e483bc5..a58b0e42 100644 --- a/livekit-rtc/livekit/rtc/_proto/track_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/track_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: track.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -17,7 +17,7 @@ from . import stats_pb2 as stats__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0btrack.proto\x12\rlivekit.proto\x1a\ne2ee.proto\x1a\x0chandle.proto\x1a\x0bstats.proto\">\n\x17\x43reateVideoTrackRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rsource_handle\x18\x02 \x01(\x04\"D\n\x18\x43reateVideoTrackResponse\x12(\n\x05track\x18\x01 \x01(\x0b\x32\x19.livekit.proto.OwnedTrack\">\n\x17\x43reateAudioTrackRequest\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x15\n\rsource_handle\x18\x02 \x01(\x04\"D\n\x18\x43reateAudioTrackResponse\x12(\n\x05track\x18\x01 \x01(\x0b\x32\x19.livekit.proto.OwnedTrack\"\'\n\x0fGetStatsRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x01(\x04\"$\n\x10GetStatsResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\"j\n\x10GetStatsCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x01(\x04\x12\x12\n\x05\x65rror\x18\x02 \x01(\tH\x00\x88\x01\x01\x12&\n\x05stats\x18\x03 \x03(\x0b\x32\x17.livekit.proto.RtcStatsB\x08\n\x06_error\"\x0c\n\nTrackEvent\"\xa3\x02\n\x14TrackPublicationInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12&\n\x04kind\x18\x03 \x01(\x0e\x32\x18.livekit.proto.TrackKind\x12*\n\x06source\x18\x04 \x01(\x0e\x32\x1a.livekit.proto.TrackSource\x12\x13\n\x0bsimulcasted\x18\x05 \x01(\x08\x12\r\n\x05width\x18\x06 \x01(\r\x12\x0e\n\x06height\x18\x07 \x01(\r\x12\x11\n\tmime_type\x18\x08 \x01(\t\x12\r\n\x05muted\x18\t \x01(\x08\x12\x0e\n\x06remote\x18\n \x01(\x08\x12\x36\n\x0f\x65ncryption_type\x18\x0b \x01(\x0e\x32\x1d.livekit.proto.EncryptionType\"y\n\x15OwnedTrackPublication\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\x31\n\x04info\x18\x02 \x01(\x0b\x32#.livekit.proto.TrackPublicationInfo\"\x9f\x01\n\tTrackInfo\x12\x0b\n\x03sid\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12&\n\x04kind\x18\x03 \x01(\x0e\x32\x18.livekit.proto.TrackKind\x12\x30\n\x0cstream_state\x18\x04 \x01(\x0e\x32\x1a.livekit.proto.StreamState\x12\r\n\x05muted\x18\x05 \x01(\x08\x12\x0e\n\x06remote\x18\x06 \x01(\x08\"c\n\nOwnedTrack\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12&\n\x04info\x18\x02 \x01(\x0b\x32\x18.livekit.proto.TrackInfo*=\n\tTrackKind\x12\x10\n\x0cKIND_UNKNOWN\x10\x00\x12\x0e\n\nKIND_AUDIO\x10\x01\x12\x0e\n\nKIND_VIDEO\x10\x02*\x81\x01\n\x0bTrackSource\x12\x12\n\x0eSOURCE_UNKNOWN\x10\x00\x12\x11\n\rSOURCE_CAMERA\x10\x01\x12\x15\n\x11SOURCE_MICROPHONE\x10\x02\x12\x16\n\x12SOURCE_SCREENSHARE\x10\x03\x12\x1c\n\x18SOURCE_SCREENSHARE_AUDIO\x10\x04*D\n\x0bStreamState\x12\x11\n\rSTATE_UNKNOWN\x10\x00\x12\x10\n\x0cSTATE_ACTIVE\x10\x01\x12\x10\n\x0cSTATE_PAUSED\x10\x02\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0btrack.proto\x12\rlivekit.proto\x1a\ne2ee.proto\x1a\x0chandle.proto\x1a\x0bstats.proto\">\n\x17\x43reateVideoTrackRequest\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x15\n\rsource_handle\x18\x02 \x02(\x04\"D\n\x18\x43reateVideoTrackResponse\x12(\n\x05track\x18\x01 \x02(\x0b\x32\x19.livekit.proto.OwnedTrack\">\n\x17\x43reateAudioTrackRequest\x12\x0c\n\x04name\x18\x01 \x02(\t\x12\x15\n\rsource_handle\x18\x02 \x02(\x04\"D\n\x18\x43reateAudioTrackResponse\x12(\n\x05track\x18\x01 \x02(\x0b\x32\x19.livekit.proto.OwnedTrack\"A\n\x0fGetStatsRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12\x18\n\x10request_async_id\x18\x02 \x01(\x04\"$\n\x10GetStatsResponse\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\"[\n\x10GetStatsCallback\x12\x10\n\x08\x61sync_id\x18\x01 \x02(\x04\x12\r\n\x05\x65rror\x18\x02 \x01(\t\x12&\n\x05stats\x18\x03 \x03(\x0b\x32\x17.livekit.proto.RtcStats\"\x0c\n\nTrackEvent\"\xa3\x03\n\x14TrackPublicationInfo\x12\x0b\n\x03sid\x18\x01 \x02(\t\x12\x0c\n\x04name\x18\x02 \x02(\t\x12&\n\x04kind\x18\x03 \x02(\x0e\x32\x18.livekit.proto.TrackKind\x12*\n\x06source\x18\x04 \x02(\x0e\x32\x1a.livekit.proto.TrackSource\x12\x13\n\x0bsimulcasted\x18\x05 \x02(\x08\x12\r\n\x05width\x18\x06 \x02(\r\x12\x0e\n\x06height\x18\x07 \x02(\r\x12\x11\n\tmime_type\x18\x08 \x02(\t\x12\r\n\x05muted\x18\t \x02(\x08\x12\x0e\n\x06remote\x18\n \x02(\x08\x12\x36\n\x0f\x65ncryption_type\x18\x0b \x02(\x0e\x32\x1d.livekit.proto.EncryptionType\x12\x38\n\x0e\x61udio_features\x18\x0c \x03(\x0e\x32 .livekit.proto.AudioTrackFeature\x12\x44\n\x17\x66rame_metadata_features\x18\r \x03(\x0e\x32#.livekit.proto.FrameMetadataFeature\"y\n\x15OwnedTrackPublication\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12\x31\n\x04info\x18\x02 \x02(\x0b\x32#.livekit.proto.TrackPublicationInfo\"\x9f\x01\n\tTrackInfo\x12\x0b\n\x03sid\x18\x01 \x02(\t\x12\x0c\n\x04name\x18\x02 \x02(\t\x12&\n\x04kind\x18\x03 \x02(\x0e\x32\x18.livekit.proto.TrackKind\x12\x30\n\x0cstream_state\x18\x04 \x02(\x0e\x32\x1a.livekit.proto.StreamState\x12\r\n\x05muted\x18\x05 \x02(\x08\x12\x0e\n\x06remote\x18\x06 \x02(\x08\"c\n\nOwnedTrack\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12&\n\x04info\x18\x02 \x02(\x0b\x32\x18.livekit.proto.TrackInfo\";\n\x15LocalTrackMuteRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12\x0c\n\x04mute\x18\x02 \x02(\x08\"\'\n\x16LocalTrackMuteResponse\x12\r\n\x05muted\x18\x01 \x02(\x08\"A\n\x18\x45nableRemoteTrackRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12\x0f\n\x07\x65nabled\x18\x02 \x02(\x08\",\n\x19\x45nableRemoteTrackResponse\x12\x0f\n\x07\x65nabled\x18\x01 \x02(\x08\"\xac\x01\n&SetTrackSubscriptionPermissionsRequest\x12 \n\x18local_participant_handle\x18\x01 \x02(\x04\x12 \n\x18\x61ll_participants_allowed\x18\x02 \x02(\x08\x12>\n\x0bpermissions\x18\x03 \x03(\x0b\x32).livekit.proto.ParticipantTrackPermission\"i\n\x1aParticipantTrackPermission\x12\x1c\n\x14participant_identity\x18\x01 \x02(\t\x12\x11\n\tallow_all\x18\x02 \x01(\x08\x12\x1a\n\x12\x61llowed_track_sids\x18\x03 \x03(\t\")\n\'SetTrackSubscriptionPermissionsResponse*=\n\tTrackKind\x12\x10\n\x0cKIND_UNKNOWN\x10\x00\x12\x0e\n\nKIND_AUDIO\x10\x01\x12\x0e\n\nKIND_VIDEO\x10\x02*\x81\x01\n\x0bTrackSource\x12\x12\n\x0eSOURCE_UNKNOWN\x10\x00\x12\x11\n\rSOURCE_CAMERA\x10\x01\x12\x15\n\x11SOURCE_MICROPHONE\x10\x02\x12\x16\n\x12SOURCE_SCREENSHARE\x10\x03\x12\x1c\n\x18SOURCE_SCREENSHARE_AUDIO\x10\x04*D\n\x0bStreamState\x12\x11\n\rSTATE_UNKNOWN\x10\x00\x12\x10\n\x0cSTATE_ACTIVE\x10\x01\x12\x10\n\x0cSTATE_PAUSED\x10\x02*\xbd\x01\n\x11\x41udioTrackFeature\x12\r\n\tTF_STEREO\x10\x00\x12\r\n\tTF_NO_DTX\x10\x01\x12\x18\n\x14TF_AUTO_GAIN_CONTROL\x10\x02\x12\x18\n\x14TF_ECHO_CANCELLATION\x10\x03\x12\x18\n\x14TF_NOISE_SUPPRESSION\x10\x04\x12\"\n\x1eTF_ENHANCED_NOISE_CANCELLATION\x10\x05\x12\x18\n\x14TF_PRECONNECT_BUFFER\x10\x06*S\n\x14\x46rameMetadataFeature\x12\x16\n\x12\x46MF_USER_TIMESTAMP\x10\x00\x12\x10\n\x0c\x46MF_FRAME_ID\x10\x01\x12\x11\n\rFMF_USER_DATA\x10\x02\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -25,12 +25,16 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_TRACKKIND']._serialized_start=1218 - _globals['_TRACKKIND']._serialized_end=1279 - _globals['_TRACKSOURCE']._serialized_start=1282 - _globals['_TRACKSOURCE']._serialized_end=1411 - _globals['_STREAMSTATE']._serialized_start=1413 - _globals['_STREAMSTATE']._serialized_end=1481 + _globals['_TRACKKIND']._serialized_start=1897 + _globals['_TRACKKIND']._serialized_end=1958 + _globals['_TRACKSOURCE']._serialized_start=1961 + _globals['_TRACKSOURCE']._serialized_end=2090 + _globals['_STREAMSTATE']._serialized_start=2092 + _globals['_STREAMSTATE']._serialized_end=2160 + _globals['_AUDIOTRACKFEATURE']._serialized_start=2163 + _globals['_AUDIOTRACKFEATURE']._serialized_end=2352 + _globals['_FRAMEMETADATAFEATURE']._serialized_start=2354 + _globals['_FRAMEMETADATAFEATURE']._serialized_end=2437 _globals['_CREATEVIDEOTRACKREQUEST']._serialized_start=69 _globals['_CREATEVIDEOTRACKREQUEST']._serialized_end=131 _globals['_CREATEVIDEOTRACKRESPONSE']._serialized_start=133 @@ -40,19 +44,33 @@ _globals['_CREATEAUDIOTRACKRESPONSE']._serialized_start=267 _globals['_CREATEAUDIOTRACKRESPONSE']._serialized_end=335 _globals['_GETSTATSREQUEST']._serialized_start=337 - _globals['_GETSTATSREQUEST']._serialized_end=376 - _globals['_GETSTATSRESPONSE']._serialized_start=378 - _globals['_GETSTATSRESPONSE']._serialized_end=414 - _globals['_GETSTATSCALLBACK']._serialized_start=416 - _globals['_GETSTATSCALLBACK']._serialized_end=522 - _globals['_TRACKEVENT']._serialized_start=524 - _globals['_TRACKEVENT']._serialized_end=536 - _globals['_TRACKPUBLICATIONINFO']._serialized_start=539 - _globals['_TRACKPUBLICATIONINFO']._serialized_end=830 - _globals['_OWNEDTRACKPUBLICATION']._serialized_start=832 - _globals['_OWNEDTRACKPUBLICATION']._serialized_end=953 - _globals['_TRACKINFO']._serialized_start=956 - _globals['_TRACKINFO']._serialized_end=1115 - _globals['_OWNEDTRACK']._serialized_start=1117 - _globals['_OWNEDTRACK']._serialized_end=1216 + _globals['_GETSTATSREQUEST']._serialized_end=402 + _globals['_GETSTATSRESPONSE']._serialized_start=404 + _globals['_GETSTATSRESPONSE']._serialized_end=440 + _globals['_GETSTATSCALLBACK']._serialized_start=442 + _globals['_GETSTATSCALLBACK']._serialized_end=533 + _globals['_TRACKEVENT']._serialized_start=535 + _globals['_TRACKEVENT']._serialized_end=547 + _globals['_TRACKPUBLICATIONINFO']._serialized_start=550 + _globals['_TRACKPUBLICATIONINFO']._serialized_end=969 + _globals['_OWNEDTRACKPUBLICATION']._serialized_start=971 + _globals['_OWNEDTRACKPUBLICATION']._serialized_end=1092 + _globals['_TRACKINFO']._serialized_start=1095 + _globals['_TRACKINFO']._serialized_end=1254 + _globals['_OWNEDTRACK']._serialized_start=1256 + _globals['_OWNEDTRACK']._serialized_end=1355 + _globals['_LOCALTRACKMUTEREQUEST']._serialized_start=1357 + _globals['_LOCALTRACKMUTEREQUEST']._serialized_end=1416 + _globals['_LOCALTRACKMUTERESPONSE']._serialized_start=1418 + _globals['_LOCALTRACKMUTERESPONSE']._serialized_end=1457 + _globals['_ENABLEREMOTETRACKREQUEST']._serialized_start=1459 + _globals['_ENABLEREMOTETRACKREQUEST']._serialized_end=1524 + _globals['_ENABLEREMOTETRACKRESPONSE']._serialized_start=1526 + _globals['_ENABLEREMOTETRACKRESPONSE']._serialized_end=1570 + _globals['_SETTRACKSUBSCRIPTIONPERMISSIONSREQUEST']._serialized_start=1573 + _globals['_SETTRACKSUBSCRIPTIONPERMISSIONSREQUEST']._serialized_end=1745 + _globals['_PARTICIPANTTRACKPERMISSION']._serialized_start=1747 + _globals['_PARTICIPANTTRACKPERMISSION']._serialized_end=1852 + _globals['_SETTRACKSUBSCRIPTIONPERMISSIONSRESPONSE']._serialized_start=1854 + _globals['_SETTRACKSUBSCRIPTIONPERMISSIONSRESPONSE']._serialized_end=1895 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/track_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/track_pb2.pyi index 2a687298..268b9a89 100644 --- a/livekit-rtc/livekit/rtc/_proto/track_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/track_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import collections.abc from . import e2ee_pb2 @@ -89,7 +90,51 @@ STATE_ACTIVE: StreamState.ValueType # 1 STATE_PAUSED: StreamState.ValueType # 2 global___StreamState = StreamState -@typing_extensions.final +class _AudioTrackFeature: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _AudioTrackFeatureEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_AudioTrackFeature.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + TF_STEREO: _AudioTrackFeature.ValueType # 0 + TF_NO_DTX: _AudioTrackFeature.ValueType # 1 + TF_AUTO_GAIN_CONTROL: _AudioTrackFeature.ValueType # 2 + TF_ECHO_CANCELLATION: _AudioTrackFeature.ValueType # 3 + TF_NOISE_SUPPRESSION: _AudioTrackFeature.ValueType # 4 + TF_ENHANCED_NOISE_CANCELLATION: _AudioTrackFeature.ValueType # 5 + TF_PRECONNECT_BUFFER: _AudioTrackFeature.ValueType # 6 + """client will buffer audio once available and send it to the server via bytes stream once connected""" + +class AudioTrackFeature(_AudioTrackFeature, metaclass=_AudioTrackFeatureEnumTypeWrapper): ... + +TF_STEREO: AudioTrackFeature.ValueType # 0 +TF_NO_DTX: AudioTrackFeature.ValueType # 1 +TF_AUTO_GAIN_CONTROL: AudioTrackFeature.ValueType # 2 +TF_ECHO_CANCELLATION: AudioTrackFeature.ValueType # 3 +TF_NOISE_SUPPRESSION: AudioTrackFeature.ValueType # 4 +TF_ENHANCED_NOISE_CANCELLATION: AudioTrackFeature.ValueType # 5 +TF_PRECONNECT_BUFFER: AudioTrackFeature.ValueType # 6 +"""client will buffer audio once available and send it to the server via bytes stream once connected""" +global___AudioTrackFeature = AudioTrackFeature + +class _FrameMetadataFeature: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _FrameMetadataFeatureEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_FrameMetadataFeature.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + FMF_USER_TIMESTAMP: _FrameMetadataFeature.ValueType # 0 + FMF_FRAME_ID: _FrameMetadataFeature.ValueType # 1 + FMF_USER_DATA: _FrameMetadataFeature.ValueType # 2 + +class FrameMetadataFeature(_FrameMetadataFeature, metaclass=_FrameMetadataFeatureEnumTypeWrapper): ... + +FMF_USER_TIMESTAMP: FrameMetadataFeature.ValueType # 0 +FMF_FRAME_ID: FrameMetadataFeature.ValueType # 1 +FMF_USER_DATA: FrameMetadataFeature.ValueType # 2 +global___FrameMetadataFeature = FrameMetadataFeature + +@typing.final class CreateVideoTrackRequest(google.protobuf.message.Message): """Create a new VideoTrack from a VideoSource""" @@ -102,14 +147,15 @@ class CreateVideoTrackRequest(google.protobuf.message.Message): def __init__( self, *, - name: builtins.str = ..., - source_handle: builtins.int = ..., + name: builtins.str | None = ..., + source_handle: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["name", b"name", "source_handle", b"source_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["name", b"name", "source_handle", b"source_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["name", b"name", "source_handle", b"source_handle"]) -> None: ... global___CreateVideoTrackRequest = CreateVideoTrackRequest -@typing_extensions.final +@typing.final class CreateVideoTrackResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -121,12 +167,12 @@ class CreateVideoTrackResponse(google.protobuf.message.Message): *, track: global___OwnedTrack | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["track", b"track"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["track", b"track"]) -> None: ... + def HasField(self, field_name: typing.Literal["track", b"track"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track", b"track"]) -> None: ... global___CreateVideoTrackResponse = CreateVideoTrackResponse -@typing_extensions.final +@typing.final class CreateAudioTrackRequest(google.protobuf.message.Message): """Create a new AudioTrack from a AudioSource""" @@ -139,14 +185,15 @@ class CreateAudioTrackRequest(google.protobuf.message.Message): def __init__( self, *, - name: builtins.str = ..., - source_handle: builtins.int = ..., + name: builtins.str | None = ..., + source_handle: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["name", b"name", "source_handle", b"source_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["name", b"name", "source_handle", b"source_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["name", b"name", "source_handle", b"source_handle"]) -> None: ... global___CreateAudioTrackRequest = CreateAudioTrackRequest -@typing_extensions.final +@typing.final class CreateAudioTrackResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -158,27 +205,31 @@ class CreateAudioTrackResponse(google.protobuf.message.Message): *, track: global___OwnedTrack | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["track", b"track"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["track", b"track"]) -> None: ... + def HasField(self, field_name: typing.Literal["track", b"track"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["track", b"track"]) -> None: ... global___CreateAudioTrackResponse = CreateAudioTrackResponse -@typing_extensions.final +@typing.final class GetStatsRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor TRACK_HANDLE_FIELD_NUMBER: builtins.int + REQUEST_ASYNC_ID_FIELD_NUMBER: builtins.int track_handle: builtins.int + request_async_id: builtins.int def __init__( self, *, - track_handle: builtins.int = ..., + track_handle: builtins.int | None = ..., + request_async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["track_handle", b"track_handle"]) -> None: ... + def HasField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["request_async_id", b"request_async_id", "track_handle", b"track_handle"]) -> None: ... global___GetStatsRequest = GetStatsRequest -@typing_extensions.final +@typing.final class GetStatsResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -187,13 +238,14 @@ class GetStatsResponse(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["async_id", b"async_id"]) -> None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id"]) -> None: ... global___GetStatsResponse = GetStatsResponse -@typing_extensions.final +@typing.final class GetStatsCallback(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -207,17 +259,16 @@ class GetStatsCallback(google.protobuf.message.Message): def __init__( self, *, - async_id: builtins.int = ..., + async_id: builtins.int | None = ..., error: builtins.str | None = ..., stats: collections.abc.Iterable[stats_pb2.RtcStats] | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "async_id", b"async_id", "error", b"error", "stats", b"stats"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["async_id", b"async_id", "error", b"error", "stats", b"stats"]) -> None: ... global___GetStatsCallback = GetStatsCallback -@typing_extensions.final +@typing.final class TrackEvent(google.protobuf.message.Message): """ Track @@ -231,7 +282,7 @@ class TrackEvent(google.protobuf.message.Message): global___TrackEvent = TrackEvent -@typing_extensions.final +@typing.final class TrackPublicationInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -246,6 +297,8 @@ class TrackPublicationInfo(google.protobuf.message.Message): MUTED_FIELD_NUMBER: builtins.int REMOTE_FIELD_NUMBER: builtins.int ENCRYPTION_TYPE_FIELD_NUMBER: builtins.int + AUDIO_FEATURES_FIELD_NUMBER: builtins.int + FRAME_METADATA_FEATURES_FIELD_NUMBER: builtins.int sid: builtins.str name: builtins.str kind: global___TrackKind.ValueType @@ -257,26 +310,33 @@ class TrackPublicationInfo(google.protobuf.message.Message): muted: builtins.bool remote: builtins.bool encryption_type: e2ee_pb2.EncryptionType.ValueType + @property + def audio_features(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___AudioTrackFeature.ValueType]: ... + @property + def frame_metadata_features(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[global___FrameMetadataFeature.ValueType]: ... def __init__( self, *, - sid: builtins.str = ..., - name: builtins.str = ..., - kind: global___TrackKind.ValueType = ..., - source: global___TrackSource.ValueType = ..., - simulcasted: builtins.bool = ..., - width: builtins.int = ..., - height: builtins.int = ..., - mime_type: builtins.str = ..., - muted: builtins.bool = ..., - remote: builtins.bool = ..., - encryption_type: e2ee_pb2.EncryptionType.ValueType = ..., + sid: builtins.str | None = ..., + name: builtins.str | None = ..., + kind: global___TrackKind.ValueType | None = ..., + source: global___TrackSource.ValueType | None = ..., + simulcasted: builtins.bool | None = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., + mime_type: builtins.str | None = ..., + muted: builtins.bool | None = ..., + remote: builtins.bool | None = ..., + encryption_type: e2ee_pb2.EncryptionType.ValueType | None = ..., + audio_features: collections.abc.Iterable[global___AudioTrackFeature.ValueType] | None = ..., + frame_metadata_features: collections.abc.Iterable[global___FrameMetadataFeature.ValueType] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["encryption_type", b"encryption_type", "height", b"height", "kind", b"kind", "mime_type", b"mime_type", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "simulcasted", b"simulcasted", "source", b"source", "width", b"width"]) -> None: ... + def HasField(self, field_name: typing.Literal["encryption_type", b"encryption_type", "height", b"height", "kind", b"kind", "mime_type", b"mime_type", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "simulcasted", b"simulcasted", "source", b"source", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["audio_features", b"audio_features", "encryption_type", b"encryption_type", "frame_metadata_features", b"frame_metadata_features", "height", b"height", "kind", b"kind", "mime_type", b"mime_type", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "simulcasted", b"simulcasted", "source", b"source", "width", b"width"]) -> None: ... global___TrackPublicationInfo = TrackPublicationInfo -@typing_extensions.final +@typing.final class OwnedTrackPublication(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -292,12 +352,12 @@ class OwnedTrackPublication(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___TrackPublicationInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedTrackPublication = OwnedTrackPublication -@typing_extensions.final +@typing.final class TrackInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -316,18 +376,19 @@ class TrackInfo(google.protobuf.message.Message): def __init__( self, *, - sid: builtins.str = ..., - name: builtins.str = ..., - kind: global___TrackKind.ValueType = ..., - stream_state: global___StreamState.ValueType = ..., - muted: builtins.bool = ..., - remote: builtins.bool = ..., + sid: builtins.str | None = ..., + name: builtins.str | None = ..., + kind: global___TrackKind.ValueType | None = ..., + stream_state: global___StreamState.ValueType | None = ..., + muted: builtins.bool | None = ..., + remote: builtins.bool | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["kind", b"kind", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "stream_state", b"stream_state"]) -> None: ... + def HasField(self, field_name: typing.Literal["kind", b"kind", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "stream_state", b"stream_state"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["kind", b"kind", "muted", b"muted", "name", b"name", "remote", b"remote", "sid", b"sid", "stream_state", b"stream_state"]) -> None: ... global___TrackInfo = TrackInfo -@typing_extensions.final +@typing.final class OwnedTrack(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -343,7 +404,141 @@ class OwnedTrack(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___TrackInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedTrack = OwnedTrack + +@typing.final +class LocalTrackMuteRequest(google.protobuf.message.Message): + """Mute/UnMute a track""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + MUTE_FIELD_NUMBER: builtins.int + track_handle: builtins.int + mute: builtins.bool + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + mute: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["mute", b"mute", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["mute", b"mute", "track_handle", b"track_handle"]) -> None: ... + +global___LocalTrackMuteRequest = LocalTrackMuteRequest + +@typing.final +class LocalTrackMuteResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + MUTED_FIELD_NUMBER: builtins.int + muted: builtins.bool + def __init__( + self, + *, + muted: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["muted", b"muted"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["muted", b"muted"]) -> None: ... + +global___LocalTrackMuteResponse = LocalTrackMuteResponse + +@typing.final +class EnableRemoteTrackRequest(google.protobuf.message.Message): + """Enable/Disable a remote track""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_HANDLE_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + track_handle: builtins.int + enabled: builtins.bool + def __init__( + self, + *, + track_handle: builtins.int | None = ..., + enabled: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled", "track_handle", b"track_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "track_handle", b"track_handle"]) -> None: ... + +global___EnableRemoteTrackRequest = EnableRemoteTrackRequest + +@typing.final +class EnableRemoteTrackResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + ENABLED_FIELD_NUMBER: builtins.int + enabled: builtins.bool + def __init__( + self, + *, + enabled: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled"]) -> None: ... + +global___EnableRemoteTrackResponse = EnableRemoteTrackResponse + +@typing.final +class SetTrackSubscriptionPermissionsRequest(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + ALL_PARTICIPANTS_ALLOWED_FIELD_NUMBER: builtins.int + PERMISSIONS_FIELD_NUMBER: builtins.int + local_participant_handle: builtins.int + all_participants_allowed: builtins.bool + @property + def permissions(self) -> google.protobuf.internal.containers.RepeatedCompositeFieldContainer[global___ParticipantTrackPermission]: ... + def __init__( + self, + *, + local_participant_handle: builtins.int | None = ..., + all_participants_allowed: builtins.bool | None = ..., + permissions: collections.abc.Iterable[global___ParticipantTrackPermission] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["all_participants_allowed", b"all_participants_allowed", "local_participant_handle", b"local_participant_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["all_participants_allowed", b"all_participants_allowed", "local_participant_handle", b"local_participant_handle", "permissions", b"permissions"]) -> None: ... + +global___SetTrackSubscriptionPermissionsRequest = SetTrackSubscriptionPermissionsRequest + +@typing.final +class ParticipantTrackPermission(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_IDENTITY_FIELD_NUMBER: builtins.int + ALLOW_ALL_FIELD_NUMBER: builtins.int + ALLOWED_TRACK_SIDS_FIELD_NUMBER: builtins.int + participant_identity: builtins.str + """The participant identity this permission applies to.""" + allow_all: builtins.bool + """Grant permission to all all tracks. Takes precedence over allowedTrackSids.""" + @property + def allowed_track_sids(self) -> google.protobuf.internal.containers.RepeatedScalarFieldContainer[builtins.str]: + """List of track sids to grant permission to.""" + + def __init__( + self, + *, + participant_identity: builtins.str | None = ..., + allow_all: builtins.bool | None = ..., + allowed_track_sids: collections.abc.Iterable[builtins.str] | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["allow_all", b"allow_all", "participant_identity", b"participant_identity"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["allow_all", b"allow_all", "allowed_track_sids", b"allowed_track_sids", "participant_identity", b"participant_identity"]) -> None: ... + +global___ParticipantTrackPermission = ParticipantTrackPermission + +@typing.final +class SetTrackSubscriptionPermissionsResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___SetTrackSubscriptionPermissionsResponse = SetTrackSubscriptionPermissionsResponse diff --git a/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.py b/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.py new file mode 100644 index 00000000..e9eba74e --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: track_publication.proto +# Protobuf Python Version: 4.25.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x17track_publication.proto\x12\rlivekit.proto\"X\n#EnableRemoteTrackPublicationRequest\x12 \n\x18track_publication_handle\x18\x01 \x02(\x04\x12\x0f\n\x07\x65nabled\x18\x02 \x02(\x08\"&\n$EnableRemoteTrackPublicationResponse\"o\n,UpdateRemoteTrackPublicationDimensionRequest\x12 \n\x18track_publication_handle\x18\x01 \x02(\x04\x12\r\n\x05width\x18\x02 \x02(\r\x12\x0e\n\x06height\x18\x03 \x02(\r\"/\n-UpdateRemoteTrackPublicationDimensionResponse\"y\n\'SetRemoteTrackPublicationQualityRequest\x12 \n\x18track_publication_handle\x18\x01 \x02(\x04\x12,\n\x07quality\x18\x02 \x02(\x0e\x32\x1b.livekit.proto.VideoQuality\"*\n(SetRemoteTrackPublicationQualityResponse*W\n\x0cVideoQuality\x12\x15\n\x11VIDEO_QUALITY_LOW\x10\x00\x12\x18\n\x14VIDEO_QUALITY_MEDIUM\x10\x01\x12\x16\n\x12VIDEO_QUALITY_HIGH\x10\x02\x42\x10\xaa\x02\rLiveKit.Proto') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'track_publication_pb2', _globals) +if _descriptor._USE_C_DESCRIPTORS == False: + _globals['DESCRIPTOR']._options = None + _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' + _globals['_VIDEOQUALITY']._serialized_start=501 + _globals['_VIDEOQUALITY']._serialized_end=588 + _globals['_ENABLEREMOTETRACKPUBLICATIONREQUEST']._serialized_start=42 + _globals['_ENABLEREMOTETRACKPUBLICATIONREQUEST']._serialized_end=130 + _globals['_ENABLEREMOTETRACKPUBLICATIONRESPONSE']._serialized_start=132 + _globals['_ENABLEREMOTETRACKPUBLICATIONRESPONSE']._serialized_end=170 + _globals['_UPDATEREMOTETRACKPUBLICATIONDIMENSIONREQUEST']._serialized_start=172 + _globals['_UPDATEREMOTETRACKPUBLICATIONDIMENSIONREQUEST']._serialized_end=283 + _globals['_UPDATEREMOTETRACKPUBLICATIONDIMENSIONRESPONSE']._serialized_start=285 + _globals['_UPDATEREMOTETRACKPUBLICATIONDIMENSIONRESPONSE']._serialized_end=332 + _globals['_SETREMOTETRACKPUBLICATIONQUALITYREQUEST']._serialized_start=334 + _globals['_SETREMOTETRACKPUBLICATIONQUALITYREQUEST']._serialized_end=455 + _globals['_SETREMOTETRACKPUBLICATIONQUALITYRESPONSE']._serialized_start=457 + _globals['_SETREMOTETRACKPUBLICATIONQUALITYRESPONSE']._serialized_end=499 +# @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.pyi new file mode 100644 index 00000000..9e70c7b4 --- /dev/null +++ b/livekit-rtc/livekit/rtc/_proto/track_publication_pb2.pyi @@ -0,0 +1,145 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +Copyright 2025 LiveKit, Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _VideoQuality: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _VideoQualityEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_VideoQuality.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + VIDEO_QUALITY_LOW: _VideoQuality.ValueType # 0 + VIDEO_QUALITY_MEDIUM: _VideoQuality.ValueType # 1 + VIDEO_QUALITY_HIGH: _VideoQuality.ValueType # 2 + +class VideoQuality(_VideoQuality, metaclass=_VideoQualityEnumTypeWrapper): + """Video quality for simulcasted tracks.""" + +VIDEO_QUALITY_LOW: VideoQuality.ValueType # 0 +VIDEO_QUALITY_MEDIUM: VideoQuality.ValueType # 1 +VIDEO_QUALITY_HIGH: VideoQuality.ValueType # 2 +global___VideoQuality = VideoQuality + +@typing.final +class EnableRemoteTrackPublicationRequest(google.protobuf.message.Message): + """Enable/Disable a remote track publication""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_PUBLICATION_HANDLE_FIELD_NUMBER: builtins.int + ENABLED_FIELD_NUMBER: builtins.int + track_publication_handle: builtins.int + enabled: builtins.bool + def __init__( + self, + *, + track_publication_handle: builtins.int | None = ..., + enabled: builtins.bool | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["enabled", b"enabled", "track_publication_handle", b"track_publication_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["enabled", b"enabled", "track_publication_handle", b"track_publication_handle"]) -> None: ... + +global___EnableRemoteTrackPublicationRequest = EnableRemoteTrackPublicationRequest + +@typing.final +class EnableRemoteTrackPublicationResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___EnableRemoteTrackPublicationResponse = EnableRemoteTrackPublicationResponse + +@typing.final +class UpdateRemoteTrackPublicationDimensionRequest(google.protobuf.message.Message): + """update a remote track publication dimension""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_PUBLICATION_HANDLE_FIELD_NUMBER: builtins.int + WIDTH_FIELD_NUMBER: builtins.int + HEIGHT_FIELD_NUMBER: builtins.int + track_publication_handle: builtins.int + width: builtins.int + height: builtins.int + def __init__( + self, + *, + track_publication_handle: builtins.int | None = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["height", b"height", "track_publication_handle", b"track_publication_handle", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["height", b"height", "track_publication_handle", b"track_publication_handle", "width", b"width"]) -> None: ... + +global___UpdateRemoteTrackPublicationDimensionRequest = UpdateRemoteTrackPublicationDimensionRequest + +@typing.final +class UpdateRemoteTrackPublicationDimensionResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___UpdateRemoteTrackPublicationDimensionResponse = UpdateRemoteTrackPublicationDimensionResponse + +@typing.final +class SetRemoteTrackPublicationQualityRequest(google.protobuf.message.Message): + """For tracks that support simulcasting, adjust subscribed quality.""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TRACK_PUBLICATION_HANDLE_FIELD_NUMBER: builtins.int + QUALITY_FIELD_NUMBER: builtins.int + track_publication_handle: builtins.int + quality: global___VideoQuality.ValueType + def __init__( + self, + *, + track_publication_handle: builtins.int | None = ..., + quality: global___VideoQuality.ValueType | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["quality", b"quality", "track_publication_handle", b"track_publication_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["quality", b"quality", "track_publication_handle", b"track_publication_handle"]) -> None: ... + +global___SetRemoteTrackPublicationQualityRequest = SetRemoteTrackPublicationQualityRequest + +@typing.final +class SetRemoteTrackPublicationQualityResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + def __init__( + self, + ) -> None: ... + +global___SetRemoteTrackPublicationQualityResponse = SetRemoteTrackPublicationQualityResponse diff --git a/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.py b/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.py index 5af87d6a..69ad2440 100644 --- a/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.py +++ b/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! # source: video_frame.proto -# Protobuf Python Version: 4.25.3 +# Protobuf Python Version: 4.25.1 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool @@ -13,9 +13,10 @@ from . import handle_pb2 as handle__pb2 +from . import track_pb2 as track__pb2 -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11video_frame.proto\x12\rlivekit.proto\x1a\x0chandle.proto\"\xb5\x01\n\x15NewVideoStreamRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x01(\x04\x12,\n\x04type\x18\x02 \x01(\x0e\x32\x1e.livekit.proto.VideoStreamType\x12\x33\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1e.livekit.proto.VideoBufferTypeH\x00\x88\x01\x01\x12\x18\n\x10normalize_stride\x18\x04 \x01(\x08\x42\t\n\x07_format\"I\n\x16NewVideoStreamResponse\x12/\n\x06stream\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedVideoStream\"\x7f\n\x15NewVideoSourceRequest\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.VideoSourceType\x12\x38\n\nresolution\x18\x02 \x01(\x0b\x32$.livekit.proto.VideoSourceResolution\"I\n\x16NewVideoSourceResponse\x12/\n\x06source\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedVideoSource\"\xa7\x01\n\x18\x43\x61ptureVideoFrameRequest\x12\x15\n\rsource_handle\x18\x01 \x01(\x04\x12.\n\x06\x62uffer\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\x12\x14\n\x0ctimestamp_us\x18\x03 \x01(\x03\x12.\n\x08rotation\x18\x04 \x01(\x0e\x32\x1c.livekit.proto.VideoRotation\"\x1b\n\x19\x43\x61ptureVideoFrameResponse\"\x87\x01\n\x13VideoConvertRequest\x12\x0e\n\x06\x66lip_y\x18\x01 \x01(\x08\x12.\n\x06\x62uffer\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\x12\x30\n\x08\x64st_type\x18\x03 \x01(\x0e\x32\x1e.livekit.proto.VideoBufferType\"e\n\x14VideoConvertResponse\x12\x12\n\x05\x65rror\x18\x01 \x01(\tH\x00\x88\x01\x01\x12/\n\x06\x62uffer\x18\x02 \x01(\x0b\x32\x1f.livekit.proto.OwnedVideoBufferB\x08\n\x06_error\"D\n\x0fVideoResolution\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\x12\x12\n\nframe_rate\x18\x03 \x01(\x01\"\x83\x02\n\x0fVideoBufferInfo\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.VideoBufferType\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x10\n\x08\x64\x61ta_ptr\x18\x04 \x01(\x04\x12\x0e\n\x06stride\x18\x06 \x01(\r\x12@\n\ncomponents\x18\x07 \x03(\x0b\x32,.livekit.proto.VideoBufferInfo.ComponentInfo\x1a?\n\rComponentInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x01(\x04\x12\x0e\n\x06stride\x18\x02 \x01(\r\x12\x0c\n\x04size\x18\x03 \x01(\r\"o\n\x10OwnedVideoBuffer\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\"?\n\x0fVideoStreamInfo\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.VideoStreamType\"o\n\x10OwnedVideoStream\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.VideoStreamInfo\"\x9f\x01\n\x10VideoStreamEvent\x12\x15\n\rstream_handle\x18\x01 \x01(\x04\x12;\n\x0e\x66rame_received\x18\x02 \x01(\x0b\x32!.livekit.proto.VideoFrameReceivedH\x00\x12,\n\x03\x65os\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.VideoStreamEOSH\x00\x42\t\n\x07message\"\x8b\x01\n\x12VideoFrameReceived\x12/\n\x06\x62uffer\x18\x01 \x01(\x0b\x32\x1f.livekit.proto.OwnedVideoBuffer\x12\x14\n\x0ctimestamp_us\x18\x02 \x01(\x03\x12.\n\x08rotation\x18\x03 \x01(\x0e\x32\x1c.livekit.proto.VideoRotation\"\x10\n\x0eVideoStreamEOS\"6\n\x15VideoSourceResolution\x12\r\n\x05width\x18\x01 \x01(\r\x12\x0e\n\x06height\x18\x02 \x01(\r\"?\n\x0fVideoSourceInfo\x12,\n\x04type\x18\x01 \x01(\x0e\x32\x1e.livekit.proto.VideoSourceType\"o\n\x10OwnedVideoSource\x12-\n\x06handle\x18\x01 \x01(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x01(\x0b\x32\x1e.livekit.proto.VideoSourceInfo*1\n\nVideoCodec\x12\x07\n\x03VP8\x10\x00\x12\x08\n\x04H264\x10\x01\x12\x07\n\x03\x41V1\x10\x02\x12\x07\n\x03VP9\x10\x03*l\n\rVideoRotation\x12\x14\n\x10VIDEO_ROTATION_0\x10\x00\x12\x15\n\x11VIDEO_ROTATION_90\x10\x01\x12\x16\n\x12VIDEO_ROTATION_180\x10\x02\x12\x16\n\x12VIDEO_ROTATION_270\x10\x03*\x81\x01\n\x0fVideoBufferType\x12\x08\n\x04RGBA\x10\x00\x12\x08\n\x04\x41\x42GR\x10\x01\x12\x08\n\x04\x41RGB\x10\x02\x12\x08\n\x04\x42GRA\x10\x03\x12\t\n\x05RGB24\x10\x04\x12\x08\n\x04I420\x10\x05\x12\t\n\x05I420A\x10\x06\x12\x08\n\x04I422\x10\x07\x12\x08\n\x04I444\x10\x08\x12\x08\n\x04I010\x10\t\x12\x08\n\x04NV12\x10\n*Y\n\x0fVideoStreamType\x12\x17\n\x13VIDEO_STREAM_NATIVE\x10\x00\x12\x16\n\x12VIDEO_STREAM_WEBGL\x10\x01\x12\x15\n\x11VIDEO_STREAM_HTML\x10\x02**\n\x0fVideoSourceType\x12\x17\n\x13VIDEO_SOURCE_NATIVE\x10\x00\x42\x10\xaa\x02\rLiveKit.Protob\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x11video_frame.proto\x12\rlivekit.proto\x1a\x0chandle.proto\x1a\x0btrack.proto\"\xc0\x01\n\x15NewVideoStreamRequest\x12\x14\n\x0ctrack_handle\x18\x01 \x02(\x04\x12,\n\x04type\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.VideoStreamType\x12.\n\x06\x66ormat\x18\x03 \x01(\x0e\x32\x1e.livekit.proto.VideoBufferType\x12\x18\n\x10normalize_stride\x18\x04 \x01(\x08\x12\x19\n\x11queue_size_frames\x18\x05 \x01(\r\"I\n\x16NewVideoStreamResponse\x12/\n\x06stream\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedVideoStream\"\x84\x02\n!VideoStreamFromParticipantRequest\x12\x1a\n\x12participant_handle\x18\x01 \x02(\x04\x12,\n\x04type\x18\x02 \x02(\x0e\x32\x1e.livekit.proto.VideoStreamType\x12\x30\n\x0ctrack_source\x18\x03 \x02(\x0e\x32\x1a.livekit.proto.TrackSource\x12.\n\x06\x66ormat\x18\x04 \x01(\x0e\x32\x1e.livekit.proto.VideoBufferType\x12\x18\n\x10normalize_stride\x18\x05 \x01(\x08\x12\x19\n\x11queue_size_frames\x18\x06 \x01(\r\"U\n\"VideoStreamFromParticipantResponse\x12/\n\x06stream\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedVideoStream\"\x96\x01\n\x15NewVideoSourceRequest\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.VideoSourceType\x12\x38\n\nresolution\x18\x02 \x02(\x0b\x32$.livekit.proto.VideoSourceResolution\x12\x15\n\ris_screencast\x18\x03 \x01(\x08\"I\n\x16NewVideoSourceResponse\x12/\n\x06source\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedVideoSource\"\xd7\x01\n\x18\x43\x61ptureVideoFrameRequest\x12\x15\n\rsource_handle\x18\x01 \x02(\x04\x12.\n\x06\x62uffer\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\x12\x14\n\x0ctimestamp_us\x18\x03 \x02(\x03\x12.\n\x08rotation\x18\x04 \x02(\x0e\x32\x1c.livekit.proto.VideoRotation\x12.\n\x08metadata\x18\x05 \x01(\x0b\x32\x1c.livekit.proto.FrameMetadata\"\x1b\n\x19\x43\x61ptureVideoFrameResponse\"\x87\x01\n\x13VideoConvertRequest\x12\x0e\n\x06\x66lip_y\x18\x01 \x01(\x08\x12.\n\x06\x62uffer\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\x12\x30\n\x08\x64st_type\x18\x03 \x02(\x0e\x32\x1e.livekit.proto.VideoBufferType\"e\n\x14VideoConvertResponse\x12\x0f\n\x05\x65rror\x18\x01 \x01(\tH\x00\x12\x31\n\x06\x62uffer\x18\x02 \x01(\x0b\x32\x1f.livekit.proto.OwnedVideoBufferH\x00\x42\t\n\x07message\"D\n\x0fVideoResolution\x12\r\n\x05width\x18\x01 \x02(\r\x12\x0e\n\x06height\x18\x02 \x02(\r\x12\x12\n\nframe_rate\x18\x03 \x02(\x01\"\x83\x02\n\x0fVideoBufferInfo\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.VideoBufferType\x12\r\n\x05width\x18\x02 \x02(\r\x12\x0e\n\x06height\x18\x03 \x02(\r\x12\x10\n\x08\x64\x61ta_ptr\x18\x04 \x02(\x04\x12\x0e\n\x06stride\x18\x06 \x01(\r\x12@\n\ncomponents\x18\x07 \x03(\x0b\x32,.livekit.proto.VideoBufferInfo.ComponentInfo\x1a?\n\rComponentInfo\x12\x10\n\x08\x64\x61ta_ptr\x18\x01 \x02(\x04\x12\x0e\n\x06stride\x18\x02 \x02(\r\x12\x0c\n\x04size\x18\x03 \x02(\r\"o\n\x10OwnedVideoBuffer\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.VideoBufferInfo\"L\n\rFrameMetadata\x12\x16\n\x0euser_timestamp\x18\x01 \x01(\x04\x12\x10\n\x08\x66rame_id\x18\x02 \x01(\r\x12\x11\n\tuser_data\x18\x03 \x01(\x0c\"?\n\x0fVideoStreamInfo\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.VideoStreamType\"o\n\x10OwnedVideoStream\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.VideoStreamInfo\"\x9f\x01\n\x10VideoStreamEvent\x12\x15\n\rstream_handle\x18\x01 \x02(\x04\x12;\n\x0e\x66rame_received\x18\x02 \x01(\x0b\x32!.livekit.proto.VideoFrameReceivedH\x00\x12,\n\x03\x65os\x18\x03 \x01(\x0b\x32\x1d.livekit.proto.VideoStreamEOSH\x00\x42\t\n\x07message\"\xbb\x01\n\x12VideoFrameReceived\x12/\n\x06\x62uffer\x18\x01 \x02(\x0b\x32\x1f.livekit.proto.OwnedVideoBuffer\x12\x14\n\x0ctimestamp_us\x18\x02 \x02(\x03\x12.\n\x08rotation\x18\x03 \x02(\x0e\x32\x1c.livekit.proto.VideoRotation\x12.\n\x08metadata\x18\x04 \x01(\x0b\x32\x1c.livekit.proto.FrameMetadata\"\x10\n\x0eVideoStreamEOS\"6\n\x15VideoSourceResolution\x12\r\n\x05width\x18\x01 \x02(\r\x12\x0e\n\x06height\x18\x02 \x02(\r\"?\n\x0fVideoSourceInfo\x12,\n\x04type\x18\x01 \x02(\x0e\x32\x1e.livekit.proto.VideoSourceType\"o\n\x10OwnedVideoSource\x12-\n\x06handle\x18\x01 \x02(\x0b\x32\x1d.livekit.proto.FfiOwnedHandle\x12,\n\x04info\x18\x02 \x02(\x0b\x32\x1e.livekit.proto.VideoSourceInfo*;\n\nVideoCodec\x12\x07\n\x03VP8\x10\x00\x12\x08\n\x04H264\x10\x01\x12\x07\n\x03\x41V1\x10\x02\x12\x07\n\x03VP9\x10\x03\x12\x08\n\x04H265\x10\x04*l\n\rVideoRotation\x12\x14\n\x10VIDEO_ROTATION_0\x10\x00\x12\x15\n\x11VIDEO_ROTATION_90\x10\x01\x12\x16\n\x12VIDEO_ROTATION_180\x10\x02\x12\x16\n\x12VIDEO_ROTATION_270\x10\x03*\x81\x01\n\x0fVideoBufferType\x12\x08\n\x04RGBA\x10\x00\x12\x08\n\x04\x41\x42GR\x10\x01\x12\x08\n\x04\x41RGB\x10\x02\x12\x08\n\x04\x42GRA\x10\x03\x12\t\n\x05RGB24\x10\x04\x12\x08\n\x04I420\x10\x05\x12\t\n\x05I420A\x10\x06\x12\x08\n\x04I422\x10\x07\x12\x08\n\x04I444\x10\x08\x12\x08\n\x04I010\x10\t\x12\x08\n\x04NV12\x10\n*Y\n\x0fVideoStreamType\x12\x17\n\x13VIDEO_STREAM_NATIVE\x10\x00\x12\x16\n\x12VIDEO_STREAM_WEBGL\x10\x01\x12\x15\n\x11VIDEO_STREAM_HTML\x10\x02**\n\x0fVideoSourceType\x12\x17\n\x13VIDEO_SOURCE_NATIVE\x10\x00\x42\x10\xaa\x02\rLiveKit.Proto') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) @@ -23,54 +24,60 @@ if _descriptor._USE_C_DESCRIPTORS == False: _globals['DESCRIPTOR']._options = None _globals['DESCRIPTOR']._serialized_options = b'\252\002\rLiveKit.Proto' - _globals['_VIDEOCODEC']._serialized_start=2132 - _globals['_VIDEOCODEC']._serialized_end=2181 - _globals['_VIDEOROTATION']._serialized_start=2183 - _globals['_VIDEOROTATION']._serialized_end=2291 - _globals['_VIDEOBUFFERTYPE']._serialized_start=2294 - _globals['_VIDEOBUFFERTYPE']._serialized_end=2423 - _globals['_VIDEOSTREAMTYPE']._serialized_start=2425 - _globals['_VIDEOSTREAMTYPE']._serialized_end=2514 - _globals['_VIDEOSOURCETYPE']._serialized_start=2516 - _globals['_VIDEOSOURCETYPE']._serialized_end=2558 - _globals['_NEWVIDEOSTREAMREQUEST']._serialized_start=51 - _globals['_NEWVIDEOSTREAMREQUEST']._serialized_end=232 - _globals['_NEWVIDEOSTREAMRESPONSE']._serialized_start=234 - _globals['_NEWVIDEOSTREAMRESPONSE']._serialized_end=307 - _globals['_NEWVIDEOSOURCEREQUEST']._serialized_start=309 - _globals['_NEWVIDEOSOURCEREQUEST']._serialized_end=436 - _globals['_NEWVIDEOSOURCERESPONSE']._serialized_start=438 - _globals['_NEWVIDEOSOURCERESPONSE']._serialized_end=511 - _globals['_CAPTUREVIDEOFRAMEREQUEST']._serialized_start=514 - _globals['_CAPTUREVIDEOFRAMEREQUEST']._serialized_end=681 - _globals['_CAPTUREVIDEOFRAMERESPONSE']._serialized_start=683 - _globals['_CAPTUREVIDEOFRAMERESPONSE']._serialized_end=710 - _globals['_VIDEOCONVERTREQUEST']._serialized_start=713 - _globals['_VIDEOCONVERTREQUEST']._serialized_end=848 - _globals['_VIDEOCONVERTRESPONSE']._serialized_start=850 - _globals['_VIDEOCONVERTRESPONSE']._serialized_end=951 - _globals['_VIDEORESOLUTION']._serialized_start=953 - _globals['_VIDEORESOLUTION']._serialized_end=1021 - _globals['_VIDEOBUFFERINFO']._serialized_start=1024 - _globals['_VIDEOBUFFERINFO']._serialized_end=1283 - _globals['_VIDEOBUFFERINFO_COMPONENTINFO']._serialized_start=1220 - _globals['_VIDEOBUFFERINFO_COMPONENTINFO']._serialized_end=1283 - _globals['_OWNEDVIDEOBUFFER']._serialized_start=1285 - _globals['_OWNEDVIDEOBUFFER']._serialized_end=1396 - _globals['_VIDEOSTREAMINFO']._serialized_start=1398 - _globals['_VIDEOSTREAMINFO']._serialized_end=1461 - _globals['_OWNEDVIDEOSTREAM']._serialized_start=1463 - _globals['_OWNEDVIDEOSTREAM']._serialized_end=1574 - _globals['_VIDEOSTREAMEVENT']._serialized_start=1577 - _globals['_VIDEOSTREAMEVENT']._serialized_end=1736 - _globals['_VIDEOFRAMERECEIVED']._serialized_start=1739 - _globals['_VIDEOFRAMERECEIVED']._serialized_end=1878 - _globals['_VIDEOSTREAMEOS']._serialized_start=1880 - _globals['_VIDEOSTREAMEOS']._serialized_end=1896 - _globals['_VIDEOSOURCERESOLUTION']._serialized_start=1898 - _globals['_VIDEOSOURCERESOLUTION']._serialized_end=1952 - _globals['_VIDEOSOURCEINFO']._serialized_start=1954 - _globals['_VIDEOSOURCEINFO']._serialized_end=2017 - _globals['_OWNEDVIDEOSOURCE']._serialized_start=2019 - _globals['_OWNEDVIDEOSOURCE']._serialized_end=2130 + _globals['_VIDEOCODEC']._serialized_start=2704 + _globals['_VIDEOCODEC']._serialized_end=2763 + _globals['_VIDEOROTATION']._serialized_start=2765 + _globals['_VIDEOROTATION']._serialized_end=2873 + _globals['_VIDEOBUFFERTYPE']._serialized_start=2876 + _globals['_VIDEOBUFFERTYPE']._serialized_end=3005 + _globals['_VIDEOSTREAMTYPE']._serialized_start=3007 + _globals['_VIDEOSTREAMTYPE']._serialized_end=3096 + _globals['_VIDEOSOURCETYPE']._serialized_start=3098 + _globals['_VIDEOSOURCETYPE']._serialized_end=3140 + _globals['_NEWVIDEOSTREAMREQUEST']._serialized_start=64 + _globals['_NEWVIDEOSTREAMREQUEST']._serialized_end=256 + _globals['_NEWVIDEOSTREAMRESPONSE']._serialized_start=258 + _globals['_NEWVIDEOSTREAMRESPONSE']._serialized_end=331 + _globals['_VIDEOSTREAMFROMPARTICIPANTREQUEST']._serialized_start=334 + _globals['_VIDEOSTREAMFROMPARTICIPANTREQUEST']._serialized_end=594 + _globals['_VIDEOSTREAMFROMPARTICIPANTRESPONSE']._serialized_start=596 + _globals['_VIDEOSTREAMFROMPARTICIPANTRESPONSE']._serialized_end=681 + _globals['_NEWVIDEOSOURCEREQUEST']._serialized_start=684 + _globals['_NEWVIDEOSOURCEREQUEST']._serialized_end=834 + _globals['_NEWVIDEOSOURCERESPONSE']._serialized_start=836 + _globals['_NEWVIDEOSOURCERESPONSE']._serialized_end=909 + _globals['_CAPTUREVIDEOFRAMEREQUEST']._serialized_start=912 + _globals['_CAPTUREVIDEOFRAMEREQUEST']._serialized_end=1127 + _globals['_CAPTUREVIDEOFRAMERESPONSE']._serialized_start=1129 + _globals['_CAPTUREVIDEOFRAMERESPONSE']._serialized_end=1156 + _globals['_VIDEOCONVERTREQUEST']._serialized_start=1159 + _globals['_VIDEOCONVERTREQUEST']._serialized_end=1294 + _globals['_VIDEOCONVERTRESPONSE']._serialized_start=1296 + _globals['_VIDEOCONVERTRESPONSE']._serialized_end=1397 + _globals['_VIDEORESOLUTION']._serialized_start=1399 + _globals['_VIDEORESOLUTION']._serialized_end=1467 + _globals['_VIDEOBUFFERINFO']._serialized_start=1470 + _globals['_VIDEOBUFFERINFO']._serialized_end=1729 + _globals['_VIDEOBUFFERINFO_COMPONENTINFO']._serialized_start=1666 + _globals['_VIDEOBUFFERINFO_COMPONENTINFO']._serialized_end=1729 + _globals['_OWNEDVIDEOBUFFER']._serialized_start=1731 + _globals['_OWNEDVIDEOBUFFER']._serialized_end=1842 + _globals['_FRAMEMETADATA']._serialized_start=1844 + _globals['_FRAMEMETADATA']._serialized_end=1920 + _globals['_VIDEOSTREAMINFO']._serialized_start=1922 + _globals['_VIDEOSTREAMINFO']._serialized_end=1985 + _globals['_OWNEDVIDEOSTREAM']._serialized_start=1987 + _globals['_OWNEDVIDEOSTREAM']._serialized_end=2098 + _globals['_VIDEOSTREAMEVENT']._serialized_start=2101 + _globals['_VIDEOSTREAMEVENT']._serialized_end=2260 + _globals['_VIDEOFRAMERECEIVED']._serialized_start=2263 + _globals['_VIDEOFRAMERECEIVED']._serialized_end=2450 + _globals['_VIDEOSTREAMEOS']._serialized_start=2452 + _globals['_VIDEOSTREAMEOS']._serialized_end=2468 + _globals['_VIDEOSOURCERESOLUTION']._serialized_start=2470 + _globals['_VIDEOSOURCERESOLUTION']._serialized_end=2524 + _globals['_VIDEOSOURCEINFO']._serialized_start=2526 + _globals['_VIDEOSOURCEINFO']._serialized_end=2589 + _globals['_OWNEDVIDEOSOURCE']._serialized_start=2591 + _globals['_OWNEDVIDEOSOURCE']._serialized_end=2702 # @@protoc_insertion_point(module_scope) diff --git a/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.pyi b/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.pyi index 393bb975..9327e799 100644 --- a/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.pyi +++ b/livekit-rtc/livekit/rtc/_proto/video_frame_pb2.pyi @@ -1,7 +1,7 @@ """ @generated by mypy-protobuf. Do not edit manually! isort:skip_file -Copyright 2023 LiveKit, Inc. +Copyright 2025 LiveKit, Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. """ + import builtins import collections.abc import google.protobuf.descriptor @@ -23,6 +24,7 @@ import google.protobuf.internal.enum_type_wrapper import google.protobuf.message from . import handle_pb2 import sys +from . import track_pb2 import typing if sys.version_info >= (3, 10): @@ -42,6 +44,7 @@ class _VideoCodecEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._Enu H264: _VideoCodec.ValueType # 1 AV1: _VideoCodec.ValueType # 2 VP9: _VideoCodec.ValueType # 3 + H265: _VideoCodec.ValueType # 4 class VideoCodec(_VideoCodec, metaclass=_VideoCodecEnumTypeWrapper): ... @@ -49,6 +52,7 @@ VP8: VideoCodec.ValueType # 0 H264: VideoCodec.ValueType # 1 AV1: VideoCodec.ValueType # 2 VP9: VideoCodec.ValueType # 3 +H265: VideoCodec.ValueType # 4 global___VideoCodec = VideoCodec class _VideoRotation: @@ -88,7 +92,10 @@ class _VideoBufferTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper I010: _VideoBufferType.ValueType # 9 NV12: _VideoBufferType.ValueType # 10 -class VideoBufferType(_VideoBufferType, metaclass=_VideoBufferTypeEnumTypeWrapper): ... +class VideoBufferType(_VideoBufferType, metaclass=_VideoBufferTypeEnumTypeWrapper): + """Values of this enum must not be changed + It is used to serialize a rtc.VideoFrame on Python + """ RGBA: VideoBufferType.ValueType # 0 ABGR: VideoBufferType.ValueType # 1 @@ -136,7 +143,7 @@ class VideoSourceType(_VideoSourceType, metaclass=_VideoSourceTypeEnumTypeWrappe VIDEO_SOURCE_NATIVE: VideoSourceType.ValueType # 0 global___VideoSourceType = VideoSourceType -@typing_extensions.final +@typing.final class NewVideoStreamRequest(google.protobuf.message.Message): """Create a new VideoStream VideoStream is used to receive video frames from a track @@ -148,27 +155,38 @@ class NewVideoStreamRequest(google.protobuf.message.Message): TYPE_FIELD_NUMBER: builtins.int FORMAT_FIELD_NUMBER: builtins.int NORMALIZE_STRIDE_FIELD_NUMBER: builtins.int + QUEUE_SIZE_FRAMES_FIELD_NUMBER: builtins.int track_handle: builtins.int type: global___VideoStreamType.ValueType format: global___VideoBufferType.ValueType """Get the frame on a specific format""" normalize_stride: builtins.bool """if true, stride will be set to width/chroma_width""" + queue_size_frames: builtins.int + """Maximum number of queued WebRTC sink frames on the receive path. Omit this + field to use the default bounded queue size of 1 frame. Set it to 0 to + request unbounded buffering. + + If your application consumes both audio and video, keep the queue sizing + strategy coordinated across both streams. Using a much larger queue, or + unbounded buffering, for only one of them can increase end-to-end latency + for that stream and cause audio/video drift. + """ def __init__( self, *, - track_handle: builtins.int = ..., - type: global___VideoStreamType.ValueType = ..., + track_handle: builtins.int | None = ..., + type: global___VideoStreamType.ValueType | None = ..., format: global___VideoBufferType.ValueType | None = ..., - normalize_stride: builtins.bool = ..., + normalize_stride: builtins.bool | None = ..., + queue_size_frames: builtins.int | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_format", b"_format", "format", b"format"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_format", b"_format", "format", b"format", "normalize_stride", b"normalize_stride", "track_handle", b"track_handle", "type", b"type"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_format", b"_format"]) -> typing_extensions.Literal["format"] | None: ... + def HasField(self, field_name: typing.Literal["format", b"format", "normalize_stride", b"normalize_stride", "queue_size_frames", b"queue_size_frames", "track_handle", b"track_handle", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["format", b"format", "normalize_stride", b"normalize_stride", "queue_size_frames", b"queue_size_frames", "track_handle", b"track_handle", "type", b"type"]) -> None: ... global___NewVideoStreamRequest = NewVideoStreamRequest -@typing_extensions.final +@typing.final class NewVideoStreamResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -180,12 +198,71 @@ class NewVideoStreamResponse(google.protobuf.message.Message): *, stream: global___OwnedVideoStream | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["stream", b"stream"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["stream", b"stream"]) -> None: ... + def HasField(self, field_name: typing.Literal["stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream", b"stream"]) -> None: ... global___NewVideoStreamResponse = NewVideoStreamResponse -@typing_extensions.final +@typing.final +class VideoStreamFromParticipantRequest(google.protobuf.message.Message): + """Request a video stream from a participant""" + + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + PARTICIPANT_HANDLE_FIELD_NUMBER: builtins.int + TYPE_FIELD_NUMBER: builtins.int + TRACK_SOURCE_FIELD_NUMBER: builtins.int + FORMAT_FIELD_NUMBER: builtins.int + NORMALIZE_STRIDE_FIELD_NUMBER: builtins.int + QUEUE_SIZE_FRAMES_FIELD_NUMBER: builtins.int + participant_handle: builtins.int + type: global___VideoStreamType.ValueType + track_source: track_pb2.TrackSource.ValueType + format: global___VideoBufferType.ValueType + normalize_stride: builtins.bool + queue_size_frames: builtins.int + """Maximum number of queued WebRTC sink frames on the receive path. Omit this + field to use the default bounded queue size of 1 frame. Set it to 0 to + request unbounded buffering. + + If your application consumes both audio and video, keep the queue sizing + strategy coordinated across both streams. Using a much larger queue, or + unbounded buffering, for only one of them can increase end-to-end latency + for that stream and cause audio/video drift. + """ + def __init__( + self, + *, + participant_handle: builtins.int | None = ..., + type: global___VideoStreamType.ValueType | None = ..., + track_source: track_pb2.TrackSource.ValueType | None = ..., + format: global___VideoBufferType.ValueType | None = ..., + normalize_stride: builtins.bool | None = ..., + queue_size_frames: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["format", b"format", "normalize_stride", b"normalize_stride", "participant_handle", b"participant_handle", "queue_size_frames", b"queue_size_frames", "track_source", b"track_source", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["format", b"format", "normalize_stride", b"normalize_stride", "participant_handle", b"participant_handle", "queue_size_frames", b"queue_size_frames", "track_source", b"track_source", "type", b"type"]) -> None: ... + +global___VideoStreamFromParticipantRequest = VideoStreamFromParticipantRequest + +@typing.final +class VideoStreamFromParticipantResponse(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + STREAM_FIELD_NUMBER: builtins.int + @property + def stream(self) -> global___OwnedVideoStream: ... + def __init__( + self, + *, + stream: global___OwnedVideoStream | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["stream", b"stream"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["stream", b"stream"]) -> None: ... + +global___VideoStreamFromParticipantResponse = VideoStreamFromParticipantResponse + +@typing.final class NewVideoSourceRequest(google.protobuf.message.Message): """Create a new VideoSource VideoSource is used to send video frame to a track @@ -195,24 +272,28 @@ class NewVideoSourceRequest(google.protobuf.message.Message): TYPE_FIELD_NUMBER: builtins.int RESOLUTION_FIELD_NUMBER: builtins.int + IS_SCREENCAST_FIELD_NUMBER: builtins.int type: global___VideoSourceType.ValueType + is_screencast: builtins.bool @property def resolution(self) -> global___VideoSourceResolution: """Used to determine which encodings to use + simulcast layers Most of the time it corresponds to the source resolution """ + def __init__( self, *, - type: global___VideoSourceType.ValueType = ..., + type: global___VideoSourceType.ValueType | None = ..., resolution: global___VideoSourceResolution | None = ..., + is_screencast: builtins.bool | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["resolution", b"resolution"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["resolution", b"resolution", "type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["is_screencast", b"is_screencast", "resolution", b"resolution", "type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["is_screencast", b"is_screencast", "resolution", b"resolution", "type", b"type"]) -> None: ... global___NewVideoSourceRequest = NewVideoSourceRequest -@typing_extensions.final +@typing.final class NewVideoSourceResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -224,12 +305,12 @@ class NewVideoSourceResponse(google.protobuf.message.Message): *, source: global___OwnedVideoSource | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["source", b"source"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["source", b"source"]) -> None: ... + def HasField(self, field_name: typing.Literal["source", b"source"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["source", b"source"]) -> None: ... global___NewVideoSourceResponse = NewVideoSourceResponse -@typing_extensions.final +@typing.final class CaptureVideoFrameRequest(google.protobuf.message.Message): """Push a frame to a VideoSource""" @@ -239,26 +320,30 @@ class CaptureVideoFrameRequest(google.protobuf.message.Message): BUFFER_FIELD_NUMBER: builtins.int TIMESTAMP_US_FIELD_NUMBER: builtins.int ROTATION_FIELD_NUMBER: builtins.int + METADATA_FIELD_NUMBER: builtins.int source_handle: builtins.int - @property - def buffer(self) -> global___VideoBufferInfo: ... timestamp_us: builtins.int """In microseconds""" rotation: global___VideoRotation.ValueType + @property + def buffer(self) -> global___VideoBufferInfo: ... + @property + def metadata(self) -> global___FrameMetadata: ... def __init__( self, *, - source_handle: builtins.int = ..., + source_handle: builtins.int | None = ..., buffer: global___VideoBufferInfo | None = ..., - timestamp_us: builtins.int = ..., - rotation: global___VideoRotation.ValueType = ..., + timestamp_us: builtins.int | None = ..., + rotation: global___VideoRotation.ValueType | None = ..., + metadata: global___FrameMetadata | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer", "rotation", b"rotation", "source_handle", b"source_handle", "timestamp_us", b"timestamp_us"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "metadata", b"metadata", "rotation", b"rotation", "source_handle", b"source_handle", "timestamp_us", b"timestamp_us"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "metadata", b"metadata", "rotation", b"rotation", "source_handle", b"source_handle", "timestamp_us", b"timestamp_us"]) -> None: ... global___CaptureVideoFrameRequest = CaptureVideoFrameRequest -@typing_extensions.final +@typing.final class CaptureVideoFrameResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -268,7 +353,7 @@ class CaptureVideoFrameResponse(google.protobuf.message.Message): global___CaptureVideoFrameResponse = CaptureVideoFrameResponse -@typing_extensions.final +@typing.final class VideoConvertRequest(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -276,22 +361,22 @@ class VideoConvertRequest(google.protobuf.message.Message): BUFFER_FIELD_NUMBER: builtins.int DST_TYPE_FIELD_NUMBER: builtins.int flip_y: builtins.bool + dst_type: global___VideoBufferType.ValueType @property def buffer(self) -> global___VideoBufferInfo: ... - dst_type: global___VideoBufferType.ValueType def __init__( self, *, - flip_y: builtins.bool = ..., + flip_y: builtins.bool | None = ..., buffer: global___VideoBufferInfo | None = ..., - dst_type: global___VideoBufferType.ValueType = ..., + dst_type: global___VideoBufferType.ValueType | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer", "dst_type", b"dst_type", "flip_y", b"flip_y"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "dst_type", b"dst_type", "flip_y", b"flip_y"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "dst_type", b"dst_type", "flip_y", b"flip_y"]) -> None: ... global___VideoConvertRequest = VideoConvertRequest -@typing_extensions.final +@typing.final class VideoConvertResponse(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -306,13 +391,13 @@ class VideoConvertResponse(google.protobuf.message.Message): error: builtins.str | None = ..., buffer: global___OwnedVideoBuffer | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["_error", b"_error", "buffer", b"buffer", "error", b"error"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["_error", b"_error", "buffer", b"buffer", "error", b"error"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["_error", b"_error"]) -> typing_extensions.Literal["error"] | None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "error", b"error", "message", b"message"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "error", b"error", "message", b"message"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["error", "buffer"] | None: ... global___VideoConvertResponse = VideoConvertResponse -@typing_extensions.final +@typing.final class VideoResolution(google.protobuf.message.Message): """ VideoFrame buffers @@ -329,19 +414,20 @@ class VideoResolution(google.protobuf.message.Message): def __init__( self, *, - width: builtins.int = ..., - height: builtins.int = ..., - frame_rate: builtins.float = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., + frame_rate: builtins.float | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["frame_rate", b"frame_rate", "height", b"height", "width", b"width"]) -> None: ... + def HasField(self, field_name: typing.Literal["frame_rate", b"frame_rate", "height", b"height", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame_rate", b"frame_rate", "height", b"height", "width", b"width"]) -> None: ... global___VideoResolution = VideoResolution -@typing_extensions.final +@typing.final class VideoBufferInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor - @typing_extensions.final + @typing.final class ComponentInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -354,11 +440,12 @@ class VideoBufferInfo(google.protobuf.message.Message): def __init__( self, *, - data_ptr: builtins.int = ..., - stride: builtins.int = ..., - size: builtins.int = ..., + data_ptr: builtins.int | None = ..., + stride: builtins.int | None = ..., + size: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["data_ptr", b"data_ptr", "size", b"size", "stride", b"stride"]) -> None: ... + def HasField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "size", b"size", "stride", b"stride"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "size", b"size", "stride", b"stride"]) -> None: ... TYPE_FIELD_NUMBER: builtins.int WIDTH_FIELD_NUMBER: builtins.int @@ -377,18 +464,19 @@ class VideoBufferInfo(google.protobuf.message.Message): def __init__( self, *, - type: global___VideoBufferType.ValueType = ..., - width: builtins.int = ..., - height: builtins.int = ..., - data_ptr: builtins.int = ..., - stride: builtins.int = ..., + type: global___VideoBufferType.ValueType | None = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., + data_ptr: builtins.int | None = ..., + stride: builtins.int | None = ..., components: collections.abc.Iterable[global___VideoBufferInfo.ComponentInfo] | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["components", b"components", "data_ptr", b"data_ptr", "height", b"height", "stride", b"stride", "type", b"type", "width", b"width"]) -> None: ... + def HasField(self, field_name: typing.Literal["data_ptr", b"data_ptr", "height", b"height", "stride", b"stride", "type", b"type", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["components", b"components", "data_ptr", b"data_ptr", "height", b"height", "stride", b"stride", "type", b"type", "width", b"width"]) -> None: ... global___VideoBufferInfo = VideoBufferInfo -@typing_extensions.final +@typing.final class OwnedVideoBuffer(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -404,12 +492,34 @@ class OwnedVideoBuffer(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___VideoBufferInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedVideoBuffer = OwnedVideoBuffer -@typing_extensions.final +@typing.final +class FrameMetadata(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + USER_TIMESTAMP_FIELD_NUMBER: builtins.int + FRAME_ID_FIELD_NUMBER: builtins.int + USER_DATA_FIELD_NUMBER: builtins.int + user_timestamp: builtins.int + frame_id: builtins.int + user_data: builtins.bytes + def __init__( + self, + *, + user_timestamp: builtins.int | None = ..., + frame_id: builtins.int | None = ..., + user_data: builtins.bytes | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["frame_id", b"frame_id", "user_data", b"user_data", "user_timestamp", b"user_timestamp"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["frame_id", b"frame_id", "user_data", b"user_data", "user_timestamp", b"user_timestamp"]) -> None: ... + +global___FrameMetadata = FrameMetadata + +@typing.final class VideoStreamInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -418,13 +528,14 @@ class VideoStreamInfo(google.protobuf.message.Message): def __init__( self, *, - type: global___VideoStreamType.ValueType = ..., + type: global___VideoStreamType.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["type", b"type"]) -> None: ... global___VideoStreamInfo = VideoStreamInfo -@typing_extensions.final +@typing.final class OwnedVideoStream(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -440,12 +551,12 @@ class OwnedVideoStream(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___VideoStreamInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedVideoStream = OwnedVideoStream -@typing_extensions.final +@typing.final class VideoStreamEvent(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -460,41 +571,45 @@ class VideoStreamEvent(google.protobuf.message.Message): def __init__( self, *, - stream_handle: builtins.int = ..., + stream_handle: builtins.int | None = ..., frame_received: global___VideoFrameReceived | None = ..., eos: global___VideoStreamEOS | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> None: ... - def WhichOneof(self, oneof_group: typing_extensions.Literal["message", b"message"]) -> typing_extensions.Literal["frame_received", "eos"] | None: ... + def HasField(self, field_name: typing.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["eos", b"eos", "frame_received", b"frame_received", "message", b"message", "stream_handle", b"stream_handle"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["frame_received", "eos"] | None: ... global___VideoStreamEvent = VideoStreamEvent -@typing_extensions.final +@typing.final class VideoFrameReceived(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor BUFFER_FIELD_NUMBER: builtins.int TIMESTAMP_US_FIELD_NUMBER: builtins.int ROTATION_FIELD_NUMBER: builtins.int - @property - def buffer(self) -> global___OwnedVideoBuffer: ... + METADATA_FIELD_NUMBER: builtins.int timestamp_us: builtins.int """In microseconds""" rotation: global___VideoRotation.ValueType + @property + def buffer(self) -> global___OwnedVideoBuffer: ... + @property + def metadata(self) -> global___FrameMetadata: ... def __init__( self, *, buffer: global___OwnedVideoBuffer | None = ..., - timestamp_us: builtins.int = ..., - rotation: global___VideoRotation.ValueType = ..., + timestamp_us: builtins.int | None = ..., + rotation: global___VideoRotation.ValueType | None = ..., + metadata: global___FrameMetadata | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["buffer", b"buffer"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["buffer", b"buffer", "rotation", b"rotation", "timestamp_us", b"timestamp_us"]) -> None: ... + def HasField(self, field_name: typing.Literal["buffer", b"buffer", "metadata", b"metadata", "rotation", b"rotation", "timestamp_us", b"timestamp_us"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["buffer", b"buffer", "metadata", b"metadata", "rotation", b"rotation", "timestamp_us", b"timestamp_us"]) -> None: ... global___VideoFrameReceived = VideoFrameReceived -@typing_extensions.final +@typing.final class VideoStreamEOS(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -504,7 +619,7 @@ class VideoStreamEOS(google.protobuf.message.Message): global___VideoStreamEOS = VideoStreamEOS -@typing_extensions.final +@typing.final class VideoSourceResolution(google.protobuf.message.Message): """ VideoSource @@ -519,14 +634,15 @@ class VideoSourceResolution(google.protobuf.message.Message): def __init__( self, *, - width: builtins.int = ..., - height: builtins.int = ..., + width: builtins.int | None = ..., + height: builtins.int | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["height", b"height", "width", b"width"]) -> None: ... + def HasField(self, field_name: typing.Literal["height", b"height", "width", b"width"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["height", b"height", "width", b"width"]) -> None: ... global___VideoSourceResolution = VideoSourceResolution -@typing_extensions.final +@typing.final class VideoSourceInfo(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -535,13 +651,14 @@ class VideoSourceInfo(google.protobuf.message.Message): def __init__( self, *, - type: global___VideoSourceType.ValueType = ..., + type: global___VideoSourceType.ValueType | None = ..., ) -> None: ... - def ClearField(self, field_name: typing_extensions.Literal["type", b"type"]) -> None: ... + def HasField(self, field_name: typing.Literal["type", b"type"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["type", b"type"]) -> None: ... global___VideoSourceInfo = VideoSourceInfo -@typing_extensions.final +@typing.final class OwnedVideoSource(google.protobuf.message.Message): DESCRIPTOR: google.protobuf.descriptor.Descriptor @@ -557,7 +674,7 @@ class OwnedVideoSource(google.protobuf.message.Message): handle: handle_pb2.FfiOwnedHandle | None = ..., info: global___VideoSourceInfo | None = ..., ) -> None: ... - def HasField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... - def ClearField(self, field_name: typing_extensions.Literal["handle", b"handle", "info", b"info"]) -> None: ... + def HasField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["handle", b"handle", "info", b"info"]) -> None: ... global___OwnedVideoSource = OwnedVideoSource diff --git a/livekit-rtc/livekit/rtc/_utils.py b/livekit-rtc/livekit/rtc/_utils.py index a5231e71..91175815 100644 --- a/livekit-rtc/livekit/rtc/_utils.py +++ b/livekit-rtc/livekit/rtc/_utils.py @@ -17,16 +17,16 @@ from collections import deque import ctypes import random -from typing import Callable, Generic, List, TypeVar +from typing import Any, Callable, Generator, Generic, List, TypeVar, Union logger = logging.getLogger("livekit") class classproperty(object): - def __init__(self, f): - self.f = classmethod(f) + def __init__(self, f: Callable[..., Any]) -> None: + self.f: Any = classmethod(f) - def __get__(self, *a): + def __get__(self, *a: Any) -> Any: return self.f.__get__(*a)() @@ -40,11 +40,36 @@ def task_done_logger(task: asyncio.Task) -> None: return -def get_address(data: memoryview) -> int: - """Get the address of a buffer using ctypes""" - nbytes = data.nbytes - buffer = (ctypes.c_int8 * nbytes).from_buffer(data) - return ctypes.addressof(buffer) +def _ensure_compatible_buffer( + data: Union[bytes, bytearray, memoryview], +) -> Union[bytes, bytearray, memoryview]: + """Validate and normalize a buffer for FFI use. + + Sliced memoryviews are materialized because get_address cannot + reliably resolve their offset for all buffer types. + """ + if isinstance(data, memoryview): + if not data.contiguous: + raise ValueError("memoryview must be contiguous") + if data.nbytes != len(data.obj): # type: ignore[arg-type] + return bytearray(data) if not data.readonly else bytes(data) + elif not isinstance(data, (bytes, bytearray)): + raise TypeError(f"expected bytes, bytearray, or memoryview, got {type(data)}") + return data + + +def get_address(data: Union[bytes, bytearray, memoryview]) -> int: + if isinstance(data, memoryview): + if not data.readonly: + return ctypes.addressof(ctypes.c_char.from_buffer(data)) + data = data.obj # type: ignore[assignment] + if isinstance(data, bytearray): + return ctypes.addressof(ctypes.c_char.from_buffer(data)) + if isinstance(data, bytes): + addr = ctypes.cast(ctypes.c_char_p(data), ctypes.c_void_p).value + assert addr is not None + return addr + raise TypeError(f"expected bytes, bytearray, or memoryview, got {type(data)}") T = TypeVar("T") @@ -58,7 +83,7 @@ def __init__(self, capacity: int = 0) -> None: def put(self, item: T) -> None: if self._capacity > 0 and len(self._queue) == self._capacity: - self._queue.pop() + self._queue.popleft() self._queue.append(item) self._event.set() @@ -121,7 +146,7 @@ async def join(self) -> None: _base62_characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" -def generate_random_base62(length=12): +def generate_random_base62(length: int = 12) -> str: """ Generate a random base62 encoded string of a specified length. @@ -130,3 +155,16 @@ def generate_random_base62(length=12): """ global _base62_characters return "".join(random.choice(_base62_characters) for _ in range(length)) + + +# adapted from https://stackoverflow.com/a/6043797 +def split_utf8(s: str, n: int) -> Generator[bytes, None, None]: + """Split UTF-8 s into chunks of maximum length n.""" + encoded = s.encode() + while len(encoded) > n: + k = n + while (encoded[k] & 0xC0) == 0x80: + k -= 1 + yield encoded[:k] + encoded = encoded[k:] + yield encoded diff --git a/livekit-rtc/livekit/rtc/apm.py b/livekit-rtc/livekit/rtc/apm.py new file mode 100644 index 00000000..9fda2ce9 --- /dev/null +++ b/livekit-rtc/livekit/rtc/apm.py @@ -0,0 +1,119 @@ +from __future__ import annotations + +from ._ffi_client import FfiClient, FfiHandle +from ._proto import ffi_pb2 as proto_ffi +from ._utils import get_address +from .audio_frame import AudioFrame + + +class AudioProcessingModule: + """ + Provides WebRTC audio processing capabilities including echo cancellation, noise suppression, + high-pass filtering, and gain control. + """ + + def __init__( + self, + *, + echo_cancellation: bool = False, + noise_suppression: bool = False, + high_pass_filter: bool = False, + auto_gain_control: bool = False, + ) -> None: + """ + Initialize an AudioProcessingModule instance with the specified audio processing features. + + Args: + echo_cancellation (bool, optional): Whether to enable echo cancellation. + noise_suppression (bool, optional): Whether to enable noise suppression. + high_pass_filter (bool, optional): Whether to enable a high-pass filter. + auto_gain_control (bool, optional): Whether to enable auto gain control. + """ + req = proto_ffi.FfiRequest() + req.new_apm.echo_canceller_enabled = echo_cancellation + req.new_apm.noise_suppression_enabled = noise_suppression + req.new_apm.high_pass_filter_enabled = high_pass_filter + req.new_apm.gain_controller_enabled = auto_gain_control + + resp = FfiClient.instance.request(req) + self._ffi_handle = FfiHandle(resp.new_apm.apm.handle.id) + + def process_stream(self, data: AudioFrame) -> None: + """ + Process the provided audio frame using the configured audio processing features. + + The input audio frame is modified in-place (if applicable) by the underlying audio + processing module (e.g., echo cancellation, noise suppression, etc.). + + Important: + Audio frames must be exactly 10 ms in duration. + """ + if isinstance(data._data, bytes) or ( + isinstance(data._data, memoryview) and data._data.readonly + ): + data._data = bytearray(data._data) + + req = proto_ffi.FfiRequest() + req.apm_process_stream.apm_handle = self._ffi_handle.handle + req.apm_process_stream.data_ptr = get_address(data._data) + req.apm_process_stream.size = len(data._data) + req.apm_process_stream.sample_rate = data.sample_rate + req.apm_process_stream.num_channels = data.num_channels + + resp = FfiClient.instance.request(req) + + if resp.apm_process_stream.error: + raise RuntimeError(resp.apm_process_stream.error) + + def process_reverse_stream(self, data: AudioFrame) -> None: + """ + Process the reverse audio frame (typically used for echo cancellation in a full-duplex setup). + + In an echo cancellation scenario, this method is used to process the "far-end" audio + prior to mixing or feeding it into the echo canceller. Like `process_stream`, the + input audio frame is modified in-place by the underlying processing module. + + Important: + Audio frames must be exactly 10 ms in duration. + """ + if isinstance(data._data, bytes) or ( + isinstance(data._data, memoryview) and data._data.readonly + ): + data._data = bytearray(data._data) + + req = proto_ffi.FfiRequest() + req.apm_process_reverse_stream.apm_handle = self._ffi_handle.handle + req.apm_process_reverse_stream.data_ptr = get_address(data._data) + req.apm_process_reverse_stream.size = len(data._data) + req.apm_process_reverse_stream.sample_rate = data.sample_rate + req.apm_process_reverse_stream.num_channels = data.num_channels + + resp = FfiClient.instance.request(req) + + if resp.apm_process_reverse_stream.error: + raise RuntimeError(resp.apm_process_reverse_stream.error) + + def set_stream_delay_ms(self, delay_ms: int) -> None: + """ + This must be called if and only if echo processing is enabled. + + Sets the `delay` in ms between `process_reverse_stream()` receiving a far-end + frame and `process_stream()` receiving a near-end frame containing the + corresponding echo. On the client-side this can be expressed as + delay = (t_render - t_analyze) + (t_process - t_capture) + where, + - t_analyze is the time a frame is passed to `process_reverse_stream()` and + t_render is the time the first sample of the same frame is rendered by + the audio hardware. + - t_capture is the time the first sample of a frame is captured by the + audio hardware and t_process is the time the same frame is passed to + `process_stream()`. + """ + req = proto_ffi.FfiRequest() + req.apm_set_stream_delay.apm_handle = self._ffi_handle.handle + req.apm_set_stream_delay.delay_ms = delay_ms + + resp = FfiClient.instance.request(req) + + if resp.apm_set_stream_delay.error: + raise RuntimeError(resp.apm_set_stream_delay.error) diff --git a/livekit-rtc/livekit/rtc/audio_filter.py b/livekit-rtc/livekit/rtc/audio_filter.py new file mode 100644 index 00000000..a6ec7296 --- /dev/null +++ b/livekit-rtc/livekit/rtc/audio_filter.py @@ -0,0 +1,23 @@ +from typing import Optional, List + +from ._ffi_client import FfiClient +from ._proto import ffi_pb2 as proto_ffi + + +class AudioFilter: + def __init__(self, module_id: str, path: str, dependencies: Optional[List[str]] = None) -> None: + self._path = path + + req = proto_ffi.FfiRequest() + req.load_audio_filter_plugin.module_id = module_id + req.load_audio_filter_plugin.plugin_path = path + req.load_audio_filter_plugin.dependencies[:] = ( + dependencies if dependencies is not None else [] + ) + + resp = FfiClient.instance.request(req) + + if resp.load_audio_filter_plugin.error: + raise Exception( + f"failed to initialize audio filter #{resp.load_audio_filter_plugin.error}" + ) diff --git a/livekit-rtc/livekit/rtc/audio_frame.py b/livekit-rtc/livekit/rtc/audio_frame.py index 956bb5e0..0a776adc 100644 --- a/livekit-rtc/livekit/rtc/audio_frame.py +++ b/livekit-rtc/livekit/rtc/audio_frame.py @@ -13,37 +13,80 @@ # limitations under the License. import ctypes -from ._ffi_client import FfiHandle, FfiClient +from ._ffi_client import FfiHandle from ._proto import audio_frame_pb2 as proto_audio -from ._proto import ffi_pb2 as proto_ffi -from ._utils import get_address -from typing import Union +from ._utils import _ensure_compatible_buffer, get_address +from typing import Any, Union class AudioFrame: + """ + A class that represents a frame of audio data with specific properties such as sample rate, + number of channels, and samples per channel. + + The format of the audio data is 16-bit signed integers (int16) interleaved by channel. + """ + def __init__( self, data: Union[bytes, bytearray, memoryview], sample_rate: int, num_channels: int, samples_per_channel: int, + *, + userdata: Union[dict[str, Any], None] = None, ) -> None: - if len(data) < num_channels * samples_per_channel * ctypes.sizeof( - ctypes.c_int16 - ): + """ + Initialize an AudioFrame instance. + + Args: + data (Union[bytes, bytearray, memoryview]): The raw audio data, which must be at least + `num_channels * samples_per_channel * sizeof(int16)` bytes long. + sample_rate (int): The sample rate of the audio in Hz. + num_channels (int): The number of audio channels (e.g., 1 for mono, 2 for stereo). + samples_per_channel (int): The number of samples per channel. + + Raises: + ValueError: If the length of `data` is smaller than the required size. + """ + if isinstance(data, memoryview): + data = data.cast("B") + + data = _ensure_compatible_buffer(data) + + min_size = num_channels * samples_per_channel * ctypes.sizeof(ctypes.c_int16) + data_len = len(data) + + if data_len < min_size: raise ValueError( "data length must be >= num_channels * samples_per_channel * sizeof(int16)" ) - self._data = bytearray(data) + if data_len % ctypes.sizeof(ctypes.c_int16) != 0: + # can happen if data is bigger than needed + raise ValueError("data length must be a multiple of sizeof(int16)") + + self._data = data + self._sample_rate = sample_rate self._num_channels = num_channels self._samples_per_channel = samples_per_channel + self._userdata = {} if userdata is None else userdata @staticmethod - def create( - sample_rate: int, num_channels: int, samples_per_channel: int - ) -> "AudioFrame": + def create(sample_rate: int, num_channels: int, samples_per_channel: int) -> "AudioFrame": + """ + Create a new empty AudioFrame instance with specified sample rate, number of channels, + and samples per channel. + + Args: + sample_rate (int): The sample rate of the audio in Hz. + num_channels (int): The number of audio channels (e.g., 1 for mono, 2 for stereo). + samples_per_channel (int): The number of samples per channel. + + Returns: + AudioFrame: A new AudioFrame instance with uninitialized (zeroed) data. + """ size = num_channels * samples_per_channel * ctypes.sizeof(ctypes.c_int16) data = bytearray(size) return AudioFrame(data, sample_rate, num_channels, samples_per_channel) @@ -54,49 +97,146 @@ def _from_owned_info(owned_info: proto_audio.OwnedAudioFrameBuffer) -> "AudioFra size = info.num_channels * info.samples_per_channel cdata = (ctypes.c_int16 * size).from_address(info.data_ptr) data = bytearray(cdata) - FfiHandle(owned_info.handle.id) - return AudioFrame( - data, info.sample_rate, info.num_channels, info.samples_per_channel - ) - - def remix_and_resample(self, sample_rate: int, num_channels: int) -> "AudioFrame": - """Resample the audio frame to the given sample rate and number of channels.""" - - req = proto_ffi.FfiRequest() - req.new_audio_resampler.CopyFrom(proto_audio.NewAudioResamplerRequest()) - - resp = FfiClient.instance.request(req) - resampler_handle = FfiHandle(resp.new_audio_resampler.resampler.handle.id) - - resample_req = proto_ffi.FfiRequest() - resample_req.remix_and_resample.resampler_handle = resampler_handle.handle - resample_req.remix_and_resample.buffer.CopyFrom(self._proto_info()) - resample_req.remix_and_resample.sample_rate = sample_rate - resample_req.remix_and_resample.num_channels = num_channels - - resp = FfiClient.instance.request(resample_req) - return AudioFrame._from_owned_info(resp.remix_and_resample.buffer) + FfiHandle(owned_info.handle.id).dispose() + return AudioFrame(data, info.sample_rate, info.num_channels, info.samples_per_channel) def _proto_info(self) -> proto_audio.AudioFrameBufferInfo: audio_info = proto_audio.AudioFrameBufferInfo() - audio_info.data_ptr = get_address(memoryview(self._data)) + audio_info.data_ptr = get_address(self._data) audio_info.sample_rate = self.sample_rate audio_info.num_channels = self.num_channels audio_info.samples_per_channel = self.samples_per_channel return audio_info + @property + def userdata(self) -> dict[str, Any]: + """ + Returns the user data associated with the audio frame. + """ + return self._userdata + @property def data(self) -> memoryview: - return memoryview(self._data).cast("h") + """ + Returns a memory view of the audio data as 16-bit signed integers. + + Returns: + memoryview: A memory view of the audio data. + """ + return memoryview(self._data).cast("B").cast("h") @property def sample_rate(self) -> int: + """ + Returns the sample rate of the audio frame. + + Returns: + int: The sample rate in Hz. + """ return self._sample_rate @property def num_channels(self) -> int: + """ + Returns the number of channels in the audio frame. + + Returns: + int: The number of audio channels (e.g., 1 for mono, 2 for stereo). + """ return self._num_channels @property def samples_per_channel(self) -> int: + """ + Returns the number of samples per channel. + + Returns: + int: The number of samples per channel. + """ return self._samples_per_channel + + @property + def duration(self) -> float: + """ + Returns the duration of the audio frame in seconds. + + Returns: + float: The duration in seconds. + """ + return self.samples_per_channel / self.sample_rate + + def to_wav_bytes(self) -> bytes: + """ + Convert the audio frame data to a WAV-formatted byte stream. + + Returns: + bytes: The audio data encoded in WAV format. + """ + import wave + import io + + with io.BytesIO() as wav_file: + with wave.open(wav_file, "wb") as wav: + wav.setnchannels(self.num_channels) + wav.setsampwidth(2) + wav.setframerate(self.sample_rate) + wav.writeframes(self._data) + + return wav_file.getvalue() + + def __repr__(self) -> str: + return ( + f"rtc.AudioFrame(sample_rate={self.sample_rate}, " + f"num_channels={self.num_channels}, " + f"samples_per_channel={self.samples_per_channel}, " + f"duration={self.duration:.3f})" + ) + + @classmethod + def __get_pydantic_core_schema__(cls, *_: Any) -> Any: + from pydantic_core import core_schema + import base64 + + def validate_audio_frame(value: Any) -> "AudioFrame": + if isinstance(value, AudioFrame): + return value + + if isinstance(value, tuple): + value = value[0] + + if isinstance(value, dict): + return AudioFrame( + data=base64.b64decode(value["data"]), + sample_rate=value["sample_rate"], + num_channels=value["num_channels"], + samples_per_channel=value["samples_per_channel"], + ) + + raise TypeError("Invalid type for AudioFrame") + + return core_schema.json_or_python_schema( + json_schema=core_schema.chain_schema( + [ + core_schema.model_fields_schema( + { + "data": core_schema.model_field(core_schema.str_schema()), + "sample_rate": core_schema.model_field(core_schema.int_schema()), + "num_channels": core_schema.model_field(core_schema.int_schema()), + "samples_per_channel": core_schema.model_field( + core_schema.int_schema() + ), + }, + ), + core_schema.no_info_plain_validator_function(validate_audio_frame), + ] + ), + python_schema=core_schema.no_info_plain_validator_function(validate_audio_frame), + serialization=core_schema.plain_serializer_function_ser_schema( + lambda instance: { + "data": base64.b64encode(instance.data).decode("utf-8"), + "sample_rate": instance.sample_rate, + "num_channels": instance.num_channels, + "samples_per_channel": instance.samples_per_channel, + } + ), + ) diff --git a/livekit-rtc/livekit/rtc/audio_mixer.py b/livekit-rtc/livekit/rtc/audio_mixer.py new file mode 100644 index 00000000..e2f28c6b --- /dev/null +++ b/livekit-rtc/livekit/rtc/audio_mixer.py @@ -0,0 +1,200 @@ +import asyncio +import numpy as np +import contextlib +from dataclasses import dataclass +from typing import AsyncIterator, Optional +from .audio_frame import AudioFrame +from .log import logger + +_Stream = AsyncIterator[AudioFrame] + + +@dataclass +class _Contribution: + stream: _Stream + data: np.ndarray + buffer: np.ndarray + had_data: bool + exhausted: bool + + +class AudioMixer: + def __init__( + self, + sample_rate: int, + num_channels: int, + *, + blocksize: int = 0, + stream_timeout_ms: int = 100, + capacity: int = 100, + ) -> None: + """ + Initialize the AudioMixer. + + The mixer accepts multiple async audio streams and mixes them into a single output stream. + Each output frame is generated with a fixed chunk size determined by the blocksize (in samples). + If blocksize is not provided (or 0), it defaults to 100ms. + + Each input stream is processed in parallel, accumulating audio data until at least one chunk + of samples is available. If an input stream does not provide data within the specified timeout, + a warning is logged. The mixer can be closed immediately + (dropping unconsumed frames) or allowed to flush remaining data using end_input(). + + Args: + sample_rate (int): The audio sample rate in Hz. + num_channels (int): The number of audio channels. + blocksize (int, optional): The size of the audio block (in samples) for mixing. If not provided, + defaults to sample_rate // 10. + stream_timeout_ms (int, optional): The maximum wait time in milliseconds for each stream to provide + audio data before timing out. Defaults to 100 ms. + capacity (int, optional): The maximum number of mixed frames to store in the output queue. + Defaults to 100. + """ + self._streams: set[_Stream] = set() + self._buffers: dict[_Stream, np.ndarray] = {} + self._sample_rate: int = sample_rate + self._num_channels: int = num_channels + self._chunk_size: int = blocksize if blocksize > 0 else int(sample_rate // 10) + self._stream_timeout_ms: int = stream_timeout_ms + self._queue: asyncio.Queue[Optional[AudioFrame]] = asyncio.Queue(maxsize=capacity) + # _ending signals that no new streams will be added, + # but we continue processing until all streams are exhausted. + self._ending: bool = False + self._mixer_task: asyncio.Task = asyncio.create_task(self._mixer()) + + def add_stream(self, stream: AsyncIterator[AudioFrame]) -> None: + """ + Add an audio stream to the mixer. + + The stream is added to the internal set of streams and an empty buffer is initialized for it, + if not already present. + + Args: + stream (AsyncIterator[AudioFrame]): An async iterator that produces AudioFrame objects. + """ + if self._ending: + raise RuntimeError("Cannot add stream after mixer has been closed") + + self._streams.add(stream) + if stream not in self._buffers: + self._buffers[stream] = np.empty((0, self._num_channels), dtype=np.int16) + + def remove_stream(self, stream: AsyncIterator[AudioFrame]) -> None: + """ + Remove an audio stream from the mixer. + + This method removes the specified stream and its associated buffer from the mixer. + + Args: + stream (AsyncIterator[AudioFrame]): The audio stream to remove. + """ + self._streams.discard(stream) + self._buffers.pop(stream, None) + + def __aiter__(self) -> "AudioMixer": + return self + + async def __anext__(self) -> AudioFrame: + item = await self._queue.get() + if item is None: + raise StopAsyncIteration + return item + + async def aclose(self) -> None: + """ + Immediately stop mixing and close the mixer. + + This cancels the mixing task, and any unconsumed output in the queue may be dropped. + """ + self._ending = True + self._mixer_task.cancel() + with contextlib.suppress(asyncio.CancelledError): + await self._mixer_task + + def end_input(self) -> None: + """ + Signal that no more streams will be added. + + This method marks the mixer as closed so that it flushes any remaining buffered output before ending. + Note that existing streams will still be processed until exhausted. + """ + self._ending = True + + async def _mixer(self) -> None: + while True: + # If we're in ending mode and there are no more streams, exit. + if self._ending and not self._streams: + break + + if not self._streams: + await asyncio.sleep(0.01) + continue + + tasks = [ + self._get_contribution( + stream, + self._buffers.get(stream, np.empty((0, self._num_channels), dtype=np.int16)), + ) + for stream in list(self._streams) + ] + results = await asyncio.gather(*tasks, return_exceptions=True) + contributions = [] + any_data = False + removals = [] + for contrib in results: + if not isinstance(contrib, _Contribution): + continue + + contributions.append(contrib.data.astype(np.float32)) + self._buffers[contrib.stream] = contrib.buffer + if contrib.had_data: + any_data = True + if contrib.exhausted and contrib.buffer.shape[0] == 0: + removals.append(contrib.stream) + + for stream in removals: + self.remove_stream(stream) + + if not any_data: + await asyncio.sleep(0.001) + continue + + mixed = np.sum(np.stack(contributions, axis=0), axis=0) + mixed = np.clip(mixed, -32768, 32767).astype(np.int16) + frame = AudioFrame( + mixed.tobytes(), self._sample_rate, self._num_channels, self._chunk_size + ) + await self._queue.put(frame) + + await self._queue.put(None) + + async def _get_contribution( + self, stream: AsyncIterator[AudioFrame], buf: np.ndarray + ) -> _Contribution: + had_data = buf.shape[0] > 0 + exhausted = False + while buf.shape[0] < self._chunk_size and not exhausted: + try: + frame = await asyncio.wait_for( + stream.__anext__(), timeout=self._stream_timeout_ms / 1000 + ) + except asyncio.TimeoutError: + logger.warning(f"AudioMixer: stream {stream} timeout, ignoring") + break + except StopAsyncIteration: + exhausted = True + break + new_data = np.frombuffer(frame.data.tobytes(), dtype=np.int16).reshape( + -1, self._num_channels + ) + buf = np.concatenate((buf, new_data), axis=0) if buf.size else new_data + had_data = True + if buf.shape[0] >= self._chunk_size: + contrib, buf = buf[: self._chunk_size], buf[self._chunk_size :] + else: + pad = np.zeros((self._chunk_size - buf.shape[0], self._num_channels), dtype=np.int16) + contrib, buf = ( + np.concatenate((buf, pad), axis=0), + np.empty((0, self._num_channels), dtype=np.int16), + ) + return _Contribution(stream, contrib, buf, had_data, exhausted) diff --git a/livekit-rtc/livekit/rtc/audio_resampler.py b/livekit-rtc/livekit/rtc/audio_resampler.py new file mode 100644 index 00000000..4d4c02c5 --- /dev/null +++ b/livekit-rtc/livekit/rtc/audio_resampler.py @@ -0,0 +1,174 @@ +from __future__ import annotations + +import ctypes +from enum import Enum, unique + +from ._proto import audio_frame_pb2 as proto_audio_frame +from ._ffi_client import FfiClient, FfiHandle +from ._proto import ffi_pb2 as proto_ffi +from ._utils import get_address +from .audio_frame import AudioFrame + + +@unique +class AudioResamplerQuality(str, Enum): + QUICK = "quick" + LOW = "low" + MEDIUM = "medium" + HIGH = "high" + VERY_HIGH = "very_high" + + +class AudioResampler: + """ + A class for resampling audio data from one sample rate to another. + + `AudioResampler` provides functionality to resample audio data from an input sample rate to an output + sample rate using the Sox resampling library. It supports multiple channels and configurable resampling quality. + """ + + def __init__( + self, + input_rate: int, + output_rate: int, + *, + num_channels: int = 1, + quality: AudioResamplerQuality = AudioResamplerQuality.MEDIUM, + ) -> None: + """ + Initialize an `AudioResampler` instance for resampling audio data. + + Args: + input_rate (int): The sample rate of the input audio data (in Hz). + output_rate (int): The desired sample rate of the output audio data (in Hz). + num_channels (int, optional): The number of audio channels (e.g., 1 for mono, 2 for stereo). Defaults to 1. + quality (AudioResamplerQuality, optional): The quality setting for the resampler. Can be one of the + `AudioResamplerQuality` enum values: `QUICK`, `LOW`, `MEDIUM`, `HIGH`, `VERY_HIGH`. Higher quality settings + result in better audio quality but require more processing power. Defaults to `AudioResamplerQuality.MEDIUM`. + + Raises: + Exception: If there is an error creating the resampler. + """ + self._input_rate = input_rate + self._output_rate = output_rate + self._num_channels = num_channels + + req = proto_ffi.FfiRequest() + req.new_sox_resampler.input_rate = input_rate + req.new_sox_resampler.output_rate = output_rate + req.new_sox_resampler.num_channels = num_channels + req.new_sox_resampler.quality_recipe = _to_proto_quality(quality) + + # not exposed for now + req.new_sox_resampler.input_data_type = ( + proto_audio_frame.SoxResamplerDataType.SOXR_DATATYPE_INT16I + ) + req.new_sox_resampler.output_data_type = ( + proto_audio_frame.SoxResamplerDataType.SOXR_DATATYPE_INT16I + ) + req.new_sox_resampler.flags = 0 # default + + resp = FfiClient.instance.request(req) + + if resp.new_sox_resampler.error: + raise Exception(resp.new_sox_resampler.error) + + self._ffi_handle = FfiHandle(resp.new_sox_resampler.resampler.handle.id) + + def push(self, data: bytearray | AudioFrame) -> list[AudioFrame]: + """ + Push audio data into the resampler and retrieve any available resampled data. + + This method accepts audio data, resamples it according to the configured input and output rates, + and returns any resampled data that is available after processing the input. + + Args: + data (bytearray | AudioFrame): The audio data to resample. This can be a `bytearray` containing + raw audio bytes in int16le format or an `AudioFrame` object. + + Returns: + list[AudioFrame]: A list of `AudioFrame` objects containing the resampled audio data. + The list may be empty if no output data is available yet. + + Raises: + Exception: If there is an error during resampling. + """ + bdata = data if isinstance(data, bytearray) else data.data.cast("b") + if not bdata: + return [] + + req = proto_ffi.FfiRequest() + req.push_sox_resampler.resampler_handle = self._ffi_handle.handle + req.push_sox_resampler.data_ptr = get_address(memoryview(bdata)) + req.push_sox_resampler.size = len(bdata) + + resp = FfiClient.instance.request(req) + + if resp.push_sox_resampler.error: + raise Exception(resp.push_sox_resampler.error) + + if not resp.push_sox_resampler.output_ptr: + return [] + + cdata = (ctypes.c_int8 * resp.push_sox_resampler.size).from_address( + resp.push_sox_resampler.output_ptr + ) + output_data = bytearray(cdata) + return [ + AudioFrame( + output_data, + self._output_rate, + self._num_channels, + len(output_data) // (self._num_channels * ctypes.sizeof(ctypes.c_int16)), + ) + ] + + def flush(self) -> list[AudioFrame]: + """ + Flush any remaining audio data through the resampler and retrieve the resampled data. + + This method should be called when no more input data will be provided to ensure that all internal + buffers are processed and all resampled data is output. + + Returns: + list[AudioFrame]: A list of `AudioFrame` objects containing the remaining resampled audio data after flushing. + The list may be empty if no output data remains. + + Raises: + Exception: If there is an error during flushing. + """ + req = proto_ffi.FfiRequest() + req.flush_sox_resampler.resampler_handle = self._ffi_handle.handle + + resp = FfiClient.instance.request(req) + + if not resp.flush_sox_resampler.output_ptr: + return [] + + cdata = (ctypes.c_int8 * resp.flush_sox_resampler.size).from_address( + resp.flush_sox_resampler.output_ptr + ) + output_data = bytearray(cdata) + return [ + AudioFrame( + output_data, + self._output_rate, + self._num_channels, + len(output_data) // (self._num_channels * ctypes.sizeof(ctypes.c_int16)), + ) + ] + + +def _to_proto_quality( + quality: AudioResamplerQuality, +) -> proto_audio_frame.SoxQualityRecipe.ValueType: + if quality == AudioResamplerQuality.QUICK: + return proto_audio_frame.SoxQualityRecipe.SOXR_QUALITY_QUICK + elif quality == AudioResamplerQuality.LOW: + return proto_audio_frame.SoxQualityRecipe.SOXR_QUALITY_LOW + elif quality == AudioResamplerQuality.MEDIUM: + return proto_audio_frame.SoxQualityRecipe.SOXR_QUALITY_MEDIUM + elif quality == AudioResamplerQuality.HIGH: + return proto_audio_frame.SoxQualityRecipe.SOXR_QUALITY_HIGH + elif quality == AudioResamplerQuality.VERY_HIGH: + return proto_audio_frame.SoxQualityRecipe.SOXR_QUALITY_VERYHIGH diff --git a/livekit-rtc/livekit/rtc/audio_source.py b/livekit-rtc/livekit/rtc/audio_source.py index 4419062a..63cc1a5d 100644 --- a/livekit-rtc/livekit/rtc/audio_source.py +++ b/livekit-rtc/livekit/rtc/audio_source.py @@ -12,6 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + +import time +import asyncio + from ._ffi_client import FfiHandle, FfiClient from ._proto import audio_frame_pb2 as proto_audio_frame from ._proto import ffi_pb2 as proto_ffi @@ -19,50 +24,158 @@ class AudioSource: - def __init__(self, sample_rate: int, num_channels: int) -> None: - req = proto_ffi.FfiRequest() - req.new_audio_source.type = ( - proto_audio_frame.AudioSourceType.AUDIO_SOURCE_NATIVE - ) - req.new_audio_source.sample_rate = sample_rate - req.new_audio_source.num_channels = num_channels + """ + Represents a real-time audio source with an internal audio queue. + + The `AudioSource` class allows you to push audio frames into a real-time audio + source, managing an internal queue of audio data up to a maximum duration defined + by `queue_size_ms`. It supports asynchronous operations to capture audio frames + and to wait for the playback of all queued audio data. + """ + def __init__( + self, + sample_rate: int, + num_channels: int, + queue_size_ms: int = 1000, + loop: asyncio.AbstractEventLoop | None = None, + ) -> None: + """ + Initializes a new instance of the audio source. + + Args: + sample_rate (int): The sample rate of the audio source in Hz. + num_channels (int): The number of audio channels. + queue_size_ms (int, optional): The buffer size of the audio queue in milliseconds. + Defaults to 1000 ms. + loop (asyncio.AbstractEventLoop, optional): The event loop to use. Defaults to + `asyncio.get_event_loop()`. + """ self._sample_rate = sample_rate self._num_channels = num_channels + self._loop = loop or asyncio.get_event_loop() + + req = proto_ffi.FfiRequest() + req.new_audio_source.type = proto_audio_frame.AudioSourceType.AUDIO_SOURCE_NATIVE + req.new_audio_source.sample_rate = sample_rate + req.new_audio_source.num_channels = num_channels + req.new_audio_source.queue_size_ms = queue_size_ms resp = FfiClient.instance.request(req) self._info = resp.new_audio_source.source self._ffi_handle = FfiHandle(self._info.handle.id) + self._last_capture = 0.0 + self._q_size = 0.0 + self._join_handle: asyncio.TimerHandle | None = None + self._join_fut: asyncio.Future[None] | None = None + @property def sample_rate(self) -> int: + """The sample rate of the audio source in Hz.""" return self._sample_rate @property def num_channels(self) -> int: + """The number of audio channels.""" return self._num_channels - async def capture_frame(self, frame: AudioFrame) -> None: - """Captures an AudioFrame. + @property + def queued_duration(self) -> float: + """The current duration (in seconds) of audio data queued for playback.""" + return max(self._q_size - time.monotonic() + self._last_capture, 0.0) - Used to push new audio data into the published Track. Audio data will - be pushed in chunks of 10ms. It'll return only when all of the data in - the buffer has been pushed. + def clear_queue(self) -> None: + """ + Clears the internal audio queue, discarding all buffered audio data. + + This method immediately removes all audio data currently queued for playback, + effectively resetting the audio source's buffer. Any audio frames that have been + captured but not yet played will be discarded. This is useful in scenarios where + you need to stop playback abruptly or prevent outdated audio data from being played. """ req = proto_ffi.FfiRequest() + req.clear_audio_buffer.source_handle = self._ffi_handle.handle + _ = FfiClient.instance.request(req) + self._release_waiter() + + async def capture_frame(self, frame: AudioFrame) -> None: + """ + Captures an `AudioFrame` and queues it for playback. + + This method is used to push new audio data into the audio source. The audio data + will be processed and queued. If the size of the audio frame exceeds the internal + queue size, the method will wait until there is enough space in the queue to + accommodate the frame. The method returns only when all of the data in the buffer + has been pushed. + Args: + frame (AudioFrame): The audio frame to capture and queue. + + Raises: + Exception: If there is an error during frame capture. + """ + + if frame.samples_per_channel == 0 or self._ffi_handle.disposed: + return + + now = time.monotonic() + elapsed = 0.0 if self._last_capture == 0.0 else now - self._last_capture + self._q_size += frame.samples_per_channel / self.sample_rate - elapsed + self._last_capture = now + + if self._join_handle: + self._join_handle.cancel() + + if self._join_fut is None: + self._join_fut = self._loop.create_future() + + self._join_handle = self._loop.call_later(self._q_size, self._release_waiter) + + req = proto_ffi.FfiRequest() req.capture_audio_frame.source_handle = self._ffi_handle.handle req.capture_audio_frame.buffer.CopyFrom(frame._proto_info()) - queue = FfiClient.instance.queue.subscribe() + queue = FfiClient.instance.queue.subscribe(loop=self._loop) try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( - lambda e: e.capture_audio_frame.async_id - == resp.capture_audio_frame.async_id + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.capture_audio_frame.async_id == resp.capture_audio_frame.async_id ) finally: FfiClient.instance.queue.unsubscribe(queue) if cb.capture_audio_frame.error: raise Exception(cb.capture_audio_frame.error) + + async def wait_for_playout(self) -> None: + """ + Waits for the audio source to finish playing out all audio data. + + This method ensures that all queued audio data has been played out before returning. + It can be used to synchronize events after audio playback or to ensure that the + audio queue is empty. + """ + + if self._join_fut is None: + return + + await asyncio.shield(self._join_fut) + + def _release_waiter(self) -> None: + if self._join_fut is None: + return # could be None when clear_queue is called + + if not self._join_fut.done(): + self._join_fut.set_result(None) + + self._last_capture = 0.0 + self._q_size = 0.0 + self._join_fut = None + + async def aclose(self) -> None: + """Close the audio source + + This method cleans up resources associated with the audio source. + """ + self._ffi_handle.dispose() diff --git a/livekit-rtc/livekit/rtc/audio_stream.py b/livekit-rtc/livekit/rtc/audio_stream.py index 0a275a35..97772f4f 100644 --- a/livekit-rtc/livekit/rtc/audio_stream.py +++ b/livekit-rtc/livekit/rtc/audio_stream.py @@ -12,79 +12,334 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import asyncio -from typing import Optional +import json +from dataclasses import dataclass +from typing import Any, AsyncIterator, Optional -from ._ffi_client import FfiHandle, FfiClient +from ._ffi_client import FfiClient, FfiHandle from ._proto import audio_frame_pb2 as proto_audio_frame from ._proto import ffi_pb2 as proto_ffi +from ._proto.track_pb2 import TrackSource from ._utils import RingQueue, task_done_logger from .audio_frame import AudioFrame +from .log import logger +from .participant import Participant from .track import Track -from dataclasses import dataclass +from .frame_processor import FrameProcessor @dataclass class AudioFrameEvent: + """An event representing a received audio frame. + + Attributes: + frame (AudioFrame): The received audio frame. + """ + frame: AudioFrame +@dataclass +class NoiseCancellationOptions: + module_id: str + options: dict[str, Any] + + class AudioStream: - """AudioStream is a stream of audio frames received from a RemoteTrack.""" + """An asynchronous audio stream for receiving audio frames from a participant or track. + + The `AudioStream` class provides an asynchronous iterator over audio frames received from + a specific track or participant. It allows you to receive audio frames in real-time with + customizable sample rates and channel configurations. + """ def __init__( self, track: Track, loop: Optional[asyncio.AbstractEventLoop] = None, capacity: int = 0, + sample_rate: int = 48000, + num_channels: int = 1, + frame_size_ms: int | None = None, + noise_cancellation: Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]] = None, + auto_close_noise_cancellation: bool = True, + **kwargs: Any, ) -> None: - self._track = track + """Initialize an `AudioStream` instance. + + Args: + track (Optional[Track]): The audio track from which to receive audio. If not provided, + you must specify `participant` and `track_source` in `kwargs`. + loop (Optional[asyncio.AbstractEventLoop], optional): The event loop to use. + Defaults to the current event loop. + capacity (int, optional): The capacity of the internal frame queue. Defaults to 0 (unbounded). + sample_rate (int, optional): The sample rate for the audio stream in Hz. + Defaults to 48000. + num_channels (int, optional): The number of audio channels. Defaults to 1. + noise_cancellation (Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]], optional): + If noise cancellation is used, pass a `NoiseCancellationOptions` or `FrameProcessor[AudioFrame]` instance + created by the noise cancellation module. + auto_close_noise_cancellation (bool): + When the audio stream closes, should the FrameProcessor's close method be run? If + False, then the frame processor can be reused with another AudioStream. + + Example: + ```python + audio_stream = AudioStream( + track=audio_track, + sample_rate=44100, + num_channels=2, + ) + + audio_stream = AudioStream.from_track( + track=audio_track, + sample_rate=44100, + num_channels=2, + ) + ``` + """ + self._track: Track | None = track + self._sample_rate = sample_rate + self._num_channels = num_channels + self._frame_size_ms = frame_size_ms self._loop = loop or asyncio.get_event_loop() - self._ffi_queue = FfiClient.instance.queue.subscribe(self._loop) - self._queue: RingQueue[AudioFrameEvent] = RingQueue(capacity) + # Only subscribe to audio_stream_event to avoid unnecessary memory allocations + # from other event types (room_event, track_event, etc.) + self._ffi_queue = FfiClient.instance.queue.subscribe( + self._loop, + filter_fn=lambda e: e.WhichOneof("message") == "audio_stream_event", + ) + self._queue: RingQueue[AudioFrameEvent | None] = RingQueue(capacity) - req = proto_ffi.FfiRequest() - new_audio_stream = req.new_audio_stream - new_audio_stream.track_handle = track._ffi_handle.handle - new_audio_stream.type = proto_audio_frame.AudioStreamType.AUDIO_STREAM_NATIVE - resp = FfiClient.instance.request(req) + self._audio_filter_module: str | None = None + self._audio_filter_options: dict[str, Any] | None = None + self._processor: FrameProcessor[AudioFrame] | None = None + self._processor_auto_close = True + if isinstance(noise_cancellation, NoiseCancellationOptions): + self._audio_filter_module = noise_cancellation.module_id + self._audio_filter_options = noise_cancellation.options + elif isinstance(noise_cancellation, FrameProcessor): + self._processor = noise_cancellation + self._processor_auto_close = auto_close_noise_cancellation - stream_info = resp.new_audio_stream.stream - self._ffi_handle = FfiHandle(stream_info.handle.id) - self._info = stream_info + stream: Any = None + if "participant" in kwargs: + stream = self._create_owned_stream_from_participant( + participant=kwargs["participant"], track_source=kwargs["track_source"] + ) + else: + stream = self._create_owned_stream() + self._ffi_handle = FfiHandle(stream.handle.id) + self._info = stream.info + # Start _run only after _ffi_handle is set, so a failure above doesn't orphan + # a task that dereferences an unassigned _ffi_handle. self._task = self._loop.create_task(self._run()) self._task.add_done_callback(task_done_logger) + if self._track is not None: + self._track._register_audio_stream(self) + + @classmethod + def from_participant( + cls, + *, + participant: Participant, + track_source: TrackSource.ValueType, + loop: Optional[asyncio.AbstractEventLoop] = None, + capacity: int = 0, + sample_rate: int = 48000, + num_channels: int = 1, + frame_size_ms: int | None = None, + noise_cancellation: Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]] = None, + auto_close_noise_cancellation: bool = True, + ) -> AudioStream: + """Create an `AudioStream` from a participant's audio track. + + Args: + participant (Participant): The participant from whom to receive audio. + track_source (TrackSource.ValueType): The source of the audio track (e.g., microphone, screen share). + loop (Optional[asyncio.AbstractEventLoop], optional): The event loop to use. Defaults to the current event loop. + capacity (int, optional): The capacity of the internal frame queue. Defaults to 0 (unbounded). + sample_rate (int, optional): The sample rate for the audio stream in Hz. Defaults to 48000. + num_channels (int, optional): The number of audio channels. Defaults to 1. + noise_cancellation (Optional[NoiseCancellationOptions], optional): + If noise cancellation is used, pass a `NoiseCancellationOptions` instance + created by the noise cancellation module. + + Returns: + AudioStream: An instance of `AudioStream` that can be used to receive audio frames. + + Example: + ```python + audio_stream = AudioStream.from_participant( + participant=participant, + track_source=TrackSource.MICROPHONE, + sample_rate=24000, + num_channels=1, + ) + ``` + """ + return AudioStream( + participant=participant, + track_source=track_source, + loop=loop, + capacity=capacity, + track=None, # type: ignore + sample_rate=sample_rate, + num_channels=num_channels, + frame_size_ms=frame_size_ms, + noise_cancellation=noise_cancellation, + auto_close_noise_cancellation=auto_close_noise_cancellation, + ) + + @classmethod + def from_track( + cls, + *, + track: Track, + loop: Optional[asyncio.AbstractEventLoop] = None, + capacity: int = 0, + sample_rate: int = 48000, + num_channels: int = 1, + frame_size_ms: int | None = None, + noise_cancellation: Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]] = None, + auto_close_noise_cancellation: bool = False, + ) -> AudioStream: + """Create an `AudioStream` from an existing audio track. + + Args: + track (Track): The audio track from which to receive audio. + loop (Optional[asyncio.AbstractEventLoop], optional): The event loop to use. Defaults to the current event loop. + capacity (int, optional): The capacity of the internal frame queue. Defaults to 0 (unbounded). + sample_rate (int, optional): The sample rate for the audio stream in Hz. Defaults to 48000. + num_channels (int, optional): The number of audio channels. Defaults to 1. + noise_cancellation (Optional[NoiseCancellationOptions | FrameProcessor[AudioFrame]], optional): + If noise cancellation is used, pass a `NoiseCancellationOptions` or `FrameProcessor[AudioFrame]` instance + created by the noise cancellation module. + auto_close_noise_cancellation (bool): + When the audio stream closes, leaves the FrameProcessor in an unclosed state so it + can be used with another AudioStream. + + Returns: + AudioStream: An instance of `AudioStream` that can be used to receive audio frames. + + Example: + ```python + audio_stream = AudioStream.from_track( + track=audio_track, + sample_rate=44100, + num_channels=2, + ) + ``` + """ + return AudioStream( + track=track, + loop=loop, + capacity=capacity, + sample_rate=sample_rate, + num_channels=num_channels, + frame_size_ms=frame_size_ms, + noise_cancellation=noise_cancellation, + auto_close_noise_cancellation=auto_close_noise_cancellation, + ) + def __del__(self) -> None: FfiClient.instance.queue.unsubscribe(self._ffi_queue) - async def _run(self): + def _create_owned_stream(self) -> Any: + assert self._track is not None + req = proto_ffi.FfiRequest() + new_audio_stream = req.new_audio_stream + new_audio_stream.track_handle = self._track._ffi_handle.handle + new_audio_stream.sample_rate = self._sample_rate + new_audio_stream.num_channels = self._num_channels + new_audio_stream.queue_size_frames = 0 + if self._frame_size_ms: + new_audio_stream.frame_size_ms = self._frame_size_ms + new_audio_stream.type = proto_audio_frame.AudioStreamType.AUDIO_STREAM_NATIVE + if self._audio_filter_module is not None: + new_audio_stream.audio_filter_module_id = self._audio_filter_module + if self._audio_filter_options is not None: + new_audio_stream.audio_filter_options = json.dumps(self._audio_filter_options) + resp = FfiClient.instance.request(req) + return resp.new_audio_stream.stream + + def _create_owned_stream_from_participant( + self, participant: Participant, track_source: TrackSource.ValueType + ) -> Any: + req = proto_ffi.FfiRequest() + audio_stream_from_participant = req.audio_stream_from_participant + audio_stream_from_participant.participant_handle = participant._ffi_handle.handle + audio_stream_from_participant.sample_rate = self._sample_rate + audio_stream_from_participant.num_channels = self._num_channels + audio_stream_from_participant.queue_size_frames = 0 + audio_stream_from_participant.type = proto_audio_frame.AudioStreamType.AUDIO_STREAM_NATIVE + audio_stream_from_participant.track_source = track_source + if self._frame_size_ms: + audio_stream_from_participant.frame_size_ms = self._frame_size_ms + + if self._audio_filter_module is not None: + audio_stream_from_participant.audio_filter_module_id = self._audio_filter_module + if self._audio_filter_options is not None: + audio_stream_from_participant.audio_filter_options = json.dumps( + self._audio_filter_options + ) + resp = FfiClient.instance.request(req) + return resp.audio_stream_from_participant.stream + + async def _run(self) -> None: while True: event = await self._ffi_queue.wait_for(self._is_event) - audio_event = event.audio_stream_event + audio_event: proto_audio_frame.AudioStreamEvent = event.audio_stream_event if audio_event.HasField("frame_received"): owned_buffer_info = audio_event.frame_received.frame frame = AudioFrame._from_owned_info(owned_buffer_info) + if self._processor is not None and self._processor.enabled: + try: + frame = self._processor._process(frame) + except Exception: + logger.warning( + "Frame processing failed, passing through original frame", + exc_info=True, + ) event = AudioFrameEvent(frame) self._queue.put(event) elif audio_event.HasField("eos"): + self._queue.put(None) break FfiClient.instance.queue.unsubscribe(self._ffi_queue) async def aclose(self) -> None: + """Asynchronously close the audio stream. + + This method cleans up resources associated with the audio stream and waits for + any pending operations to complete. + """ + if self._track is not None: + self._track._unregister_audio_stream(self) self._ffi_handle.dispose() await self._task - - def __aiter__(self) -> "AudioStream": - return self + if self._processor is not None and self._processor_auto_close: + self._processor._close() def _is_event(self, e: proto_ffi.FfiEvent) -> bool: return e.audio_stream_event.stream_handle == self._ffi_handle.handle + def __aiter__(self) -> AsyncIterator[AudioFrameEvent]: + return self + async def __anext__(self) -> AudioFrameEvent: if self._task.done(): raise StopAsyncIteration - return await self._queue.get() + + item = await self._queue.get() + if item is None: + raise StopAsyncIteration + + return item diff --git a/livekit-rtc/livekit/rtc/chat.py b/livekit-rtc/livekit/rtc/chat.py deleted file mode 100644 index 5bcae348..00000000 --- a/livekit-rtc/livekit/rtc/chat.py +++ /dev/null @@ -1,138 +0,0 @@ -# Copyright 2023 LiveKit, Inc. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from dataclasses import dataclass, field -from datetime import datetime -import json -import logging -from typing import Any, Callable, Dict, Literal, Optional - -from .room import Room, Participant, DataPacket -from ._event_emitter import EventEmitter -from ._proto.room_pb2 import DataPacketKind -from ._utils import generate_random_base62 - -_CHAT_TOPIC = "lk-chat-topic" -_CHAT_UPDATE_TOPIC = "lk-chat-update-topic" - -EventTypes = Literal["message_received",] - - -class ChatManager(EventEmitter[EventTypes]): - """A utility class that sends and receives chat messages in the active session. - - It implements LiveKit Chat Protocol, and serializes data to/from JSON data packets. - """ - - def __init__(self, room: Room): - super().__init__() - self._lp = room.local_participant - self._room = room - - room.on("data_received", self._on_data_received) - - def close(self): - self._room.off("data_received", self._on_data_received) - - async def send_message(self, message: str) -> "ChatMessage": - """Send a chat message to the end user using LiveKit Chat Protocol. - - Args: - message (str): the message to send - - Returns: - ChatMessage: the message that was sent - """ - msg = ChatMessage( - message=message, - is_local=True, - participant=self._lp, - ) - await self._lp.publish_data( - payload=json.dumps(msg.asjsondict()), - kind=DataPacketKind.KIND_RELIABLE, - topic=_CHAT_TOPIC, - ) - return msg - - async def update_message(self, message: "ChatMessage"): - """Update a chat message that was previously sent. - - If message.deleted is set to True, we'll signal to remote participants that the message - should be deleted. - """ - await self._lp.publish_data( - payload=json.dumps(message.asjsondict()), - kind=DataPacketKind.KIND_RELIABLE, - topic=_CHAT_UPDATE_TOPIC, - ) - - def on_message(self, callback: Callable[["ChatMessage"], None]): - """Register a callback to be called when a chat message is received from the end user.""" - self._callback = callback - - def _on_data_received(self, dp: DataPacket): - # handle both new and updates the same way, as long as the ID is in there - # the user can decide how to replace the previous message - if dp.topic == _CHAT_TOPIC or dp.topic == _CHAT_UPDATE_TOPIC: - try: - parsed = json.loads(dp.data) - msg = ChatMessage.from_jsondict(parsed) - if dp.participant: - msg.participant = dp.participant - self.emit("message_received", msg) - except Exception as e: - logging.warning("failed to parse chat message: %s", e, exc_info=e) - - -@dataclass -class ChatMessage: - message: Optional[str] = None - id: str = field(default_factory=generate_random_base62) - timestamp: datetime = field(default_factory=datetime.now) - deleted: bool = field(default=False) - - # These fields are not part of the wire protocol. They are here to provide - # context for the application. - participant: Optional[Participant] = None - is_local: bool = field(default=False) - - @classmethod - def from_jsondict(cls, d: Dict[str, Any]) -> "ChatMessage": - # older version of the protocol didn't contain a message ID, so we'll create one - id = d.get("id") or generate_random_base62() - timestamp = datetime.now() - if d.get("timestamp"): - timestamp = datetime.fromtimestamp(d.get("timestamp", 0) / 1000.0) - msg = cls( - id=id, - timestamp=timestamp, - ) - msg.update_from_jsondict(d) - return msg - - def update_from_jsondict(self, d: Dict[str, Any]) -> None: - self.message = d.get("message") - self.deleted = d.get("deleted", False) - - def asjsondict(self): - """Returns a JSON serializable dictionary representation of the message.""" - d = { - "id": self.id, - "message": self.message, - "timestamp": int(self.timestamp.timestamp() * 1000), - } - if self.deleted: - d["deleted"] = True - return d diff --git a/livekit-rtc/livekit/rtc/data_stream.py b/livekit-rtc/livekit/rtc/data_stream.py new file mode 100644 index 00000000..bf62a217 --- /dev/null +++ b/livekit-rtc/livekit/rtc/data_stream.py @@ -0,0 +1,359 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import asyncio +import uuid +import datetime +from collections.abc import Callable +from dataclasses import dataclass +from typing import AsyncIterator, Optional, Dict, List +from ._proto.room_pb2 import DataStream as proto_DataStream +from ._proto import ffi_pb2 as proto_ffi +from ._proto import room_pb2 as proto_room +from ._ffi_client import FfiClient +from ._utils import split_utf8 +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from .participant import LocalParticipant + +STREAM_CHUNK_SIZE = 15_000 + + +@dataclass +class BaseStreamInfo: + stream_id: str + mime_type: str + topic: str + timestamp: int + size: Optional[int] + attributes: Optional[Dict[str, str]] # Optional for the attributes dictionary + + +@dataclass +class TextStreamInfo(BaseStreamInfo): + attachments: List[str] + + +class TextStreamReader: + def __init__( + self, + header: proto_DataStream.Header, + ) -> None: + self._header = header + self._info = TextStreamInfo( + stream_id=header.stream_id, + mime_type=header.mime_type, + topic=header.topic, + timestamp=header.timestamp, + size=header.total_length, + attributes=dict(header.attributes), + attachments=list(header.text_header.attached_stream_ids), + ) + self._queue: asyncio.Queue[proto_DataStream.Chunk | None] = asyncio.Queue() + + async def _on_chunk_update(self, chunk: proto_DataStream.Chunk) -> None: + await self._queue.put(chunk) + + async def _on_stream_close(self, trailer: proto_DataStream.Trailer) -> None: + self.info.attributes = self.info.attributes or {} + self.info.attributes.update(trailer.attributes) + await self._queue.put(None) + + def __aiter__(self) -> AsyncIterator[str]: + return self + + async def __anext__(self) -> str: + item = await self._queue.get() + if item is None: + raise StopAsyncIteration + decodedStr = item.content.decode() + return decodedStr + + @property + def info(self) -> TextStreamInfo: + return self._info + + async def read_all(self) -> str: + final_string = "" + async for chunk in self: + final_string += chunk + return final_string + + +@dataclass +class ByteStreamInfo(BaseStreamInfo): + name: str + + +class ByteStreamReader: + def __init__(self, header: proto_DataStream.Header, capacity: int = 0) -> None: + self._header = header + self._info = ByteStreamInfo( + stream_id=header.stream_id, + mime_type=header.mime_type, + topic=header.topic, + timestamp=header.timestamp, + size=header.total_length, + attributes=dict(header.attributes), + name=header.byte_header.name, + ) + self._queue: asyncio.Queue[proto_DataStream.Chunk | None] = asyncio.Queue(capacity) + + async def _on_chunk_update(self, chunk: proto_DataStream.Chunk) -> None: + await self._queue.put(chunk) + + async def _on_stream_close(self, trailer: proto_DataStream.Trailer) -> None: + self.info.attributes = self.info.attributes or {} + self.info.attributes.update(trailer.attributes) + await self._queue.put(None) + + def __aiter__(self) -> AsyncIterator[bytes]: + return self + + async def __anext__(self) -> bytes: + item = await self._queue.get() + if item is None: + raise StopAsyncIteration + + return item.content + + @property + def info(self) -> ByteStreamInfo: + return self._info + + +class BaseStreamWriter: + def __init__( + self, + local_participant: LocalParticipant, + topic: str = "", + attributes: Optional[Dict[str, str]] = {}, + stream_id: str | None = None, + total_size: int | None = None, + mime_type: str = "", + destination_identities: Optional[List[str]] = None, + sender_identity: str | None = None, + ): + self._local_participant = local_participant + if stream_id is None: + stream_id = str(uuid.uuid4()) + timestamp = int(datetime.datetime.now().timestamp() * 1000) + self._header = proto_DataStream.Header( + stream_id=stream_id, + timestamp=timestamp, + mime_type=mime_type, + topic=topic, + attributes=attributes, + total_length=total_size, + ) + self._next_chunk_index: int = 0 + self._destination_identities = destination_identities + self._sender_identity = sender_identity or self._local_participant.identity + self._closed = False + + async def _send_header(self) -> None: + req = proto_ffi.FfiRequest( + send_stream_header=proto_room.SendStreamHeaderRequest( + header=self._header, + local_participant_handle=self._local_participant._ffi_handle.handle, + destination_identities=self._destination_identities, + sender_identity=self._sender_identity, + ) + ) + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.send_stream_header.async_id == resp.send_stream_header.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.send_stream_header.error: + raise ConnectionError(cb.send_stream_header.error) + + async def _send_chunk(self, chunk: proto_DataStream.Chunk) -> None: + if self._closed: + raise RuntimeError(f"Cannot send chunk after stream is closed: {chunk}") + req = proto_ffi.FfiRequest( + send_stream_chunk=proto_room.SendStreamChunkRequest( + chunk=chunk, + local_participant_handle=self._local_participant._ffi_handle.handle, + sender_identity=self._local_participant.identity, + destination_identities=self._destination_identities, + ) + ) + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.send_stream_chunk.async_id == resp.send_stream_chunk.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.send_stream_chunk.error: + raise ConnectionError(cb.send_stream_chunk.error) + + async def _send_trailer(self, trailer: proto_DataStream.Trailer) -> None: + req = proto_ffi.FfiRequest( + send_stream_trailer=proto_room.SendStreamTrailerRequest( + trailer=trailer, + local_participant_handle=self._local_participant._ffi_handle.handle, + sender_identity=self._local_participant.identity, + ) + ) + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.send_stream_trailer.async_id == resp.send_stream_trailer.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.send_stream_trailer.error: + raise ConnectionError(cb.send_stream_trailer.error) + + async def aclose( + self, *, reason: str = "", attributes: Optional[Dict[str, str]] = None + ) -> None: + if self._closed: + raise RuntimeError("Stream already closed") + self._closed = True + await self._send_trailer( + trailer=proto_DataStream.Trailer( + stream_id=self._header.stream_id, reason=reason, attributes=attributes + ) + ) + + +class TextStreamWriter(BaseStreamWriter): + def __init__( + self, + local_participant: LocalParticipant, + *, + topic: str = "", + attributes: Optional[Dict[str, str]] = {}, + stream_id: str | None = None, + total_size: int | None = None, + reply_to_id: str | None = None, + destination_identities: Optional[List[str]] = None, + sender_identity: str | None = None, + ) -> None: + super().__init__( + local_participant, + topic, + attributes, + stream_id, + total_size, + mime_type="text/plain", + destination_identities=destination_identities, + sender_identity=sender_identity, + ) + self._header.text_header.operation_type = proto_DataStream.OperationType.CREATE + if reply_to_id: + self._header.text_header.reply_to_stream_id = reply_to_id + self._info = TextStreamInfo( + stream_id=self._header.stream_id, + mime_type=self._header.mime_type, + topic=self._header.topic, + timestamp=self._header.timestamp, + size=self._header.total_length, + attributes=dict(self._header.attributes), + attachments=list(self._header.text_header.attached_stream_ids), + ) + self._write_lock = asyncio.Lock() + + async def write(self, text: str) -> None: + async with self._write_lock: + for chunk in split_utf8(text, STREAM_CHUNK_SIZE): + content = chunk + chunk_index = self._next_chunk_index + self._next_chunk_index += 1 + chunk_msg = proto_DataStream.Chunk( + stream_id=self._header.stream_id, + chunk_index=chunk_index, + content=content, + ) + await self._send_chunk(chunk_msg) + + @property + def info(self) -> TextStreamInfo: + return self._info + + +class ByteStreamWriter(BaseStreamWriter): + def __init__( + self, + local_participant: LocalParticipant, + *, + name: str, + topic: str = "", + attributes: Optional[Dict[str, str]] = None, + stream_id: str | None = None, + total_size: int | None = None, + mime_type: str = "application/octet-stream", + destination_identities: Optional[List[str]] = None, + ) -> None: + super().__init__( + local_participant, + topic, + attributes, + stream_id, + total_size, + mime_type=mime_type, + destination_identities=destination_identities, + ) + self._header.byte_header.name = name + self._info = ByteStreamInfo( + stream_id=self._header.stream_id, + mime_type=self._header.mime_type, + topic=self._header.topic, + timestamp=self._header.timestamp, + size=self._header.total_length, + attributes=dict(self._header.attributes), + name=self._header.byte_header.name, + ) + self._write_lock = asyncio.Lock() + + async def write(self, data: bytes) -> None: + async with self._write_lock: + chunked_data = [ + data[i : i + STREAM_CHUNK_SIZE] for i in range(0, len(data), STREAM_CHUNK_SIZE) + ] + + for chunk in chunked_data: + chunk_msg = proto_DataStream.Chunk( + stream_id=self._header.stream_id, + chunk_index=self._next_chunk_index, + content=chunk, + ) + await self._send_chunk(chunk_msg) + self._next_chunk_index += 1 + + @property + def info(self) -> ByteStreamInfo: + return self._info + + +TextStreamHandler = Callable[[TextStreamReader, str], None] +ByteStreamHandler = Callable[[ByteStreamReader, str], None] diff --git a/livekit-rtc/livekit/rtc/data_track.py b/livekit-rtc/livekit/rtc/data_track.py new file mode 100644 index 00000000..78aaa9a6 --- /dev/null +++ b/livekit-rtc/livekit/rtc/data_track.py @@ -0,0 +1,296 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from dataclasses import dataclass +from typing import AsyncIterator, Optional + +from ._ffi_client import FfiClient, FfiHandle +from ._proto import ffi_pb2 as proto_ffi +from ._proto import data_track_pb2 as proto_data_track + + +class SubscribeDataTrackError(Exception): + """An error that can occur when subscribing to a data track.""" + + def __init__(self, message: str) -> None: + self.message = message + + +class PushFrameError(Exception): + """Frame could not be pushed to a data track. + + Pushing a frame can fail for several reasons: + + - The track has been unpublished by the local participant or SFU + - The room is no longer connected + - Frames are being pushed too fast + """ + + def __init__(self, message: str) -> None: + self.message = message + + +@dataclass +class DataTrackInfo: + """Information about a published data track.""" + + sid: str + """Unique track identifier assigned by the SFU. + + This identifier may change if a reconnect occurs. Use ``name`` if a + stable identifier is needed. + """ + + name: str + """Name of the track assigned by the publisher.""" + + uses_e2ee: bool + """Whether or not frames sent on the track use end-to-end encryption.""" + + +@dataclass +class DataTrackFrame: + """A frame published on a data track, consisting of a payload and optional metadata.""" + + payload: bytes + """The frame's payload.""" + + user_timestamp: Optional[int] = None + """The frame's user timestamp, if one is associated.""" + + +class LocalDataTrack: + """Data track published by the local participant.""" + + def __init__(self, owned_info: proto_data_track.OwnedLocalDataTrack) -> None: + self._info = DataTrackInfo( + sid=owned_info.info.sid, + name=owned_info.info.name, + uses_e2ee=owned_info.info.uses_e2ee, + ) + self._ffi_handle = FfiHandle(owned_info.handle.id) + + @property + def info(self) -> DataTrackInfo: + """Information about the data track.""" + return self._info + + def try_push(self, frame: DataTrackFrame) -> None: + """Try pushing a frame to subscribers of the track. + + See :class:`DataTrackFrame` for how to construct a frame and attach metadata. + + Args: + frame: The data track frame to send. + + Raises: + PushFrameError: If the push fails. + """ + proto_frame = proto_data_track.DataTrackFrame(payload=bytes(frame.payload)) + if frame.user_timestamp is not None: + proto_frame.user_timestamp = frame.user_timestamp + + req = proto_ffi.FfiRequest() + req.local_data_track_try_push.track_handle = self._ffi_handle.handle + req.local_data_track_try_push.frame.CopyFrom(proto_frame) + + resp = FfiClient.instance.request(req) + if resp.local_data_track_try_push.HasField("error"): + raise PushFrameError(resp.local_data_track_try_push.error.message) + + def is_published(self) -> bool: + """Whether or not the track is still published.""" + req = proto_ffi.FfiRequest() + req.local_data_track_is_published.track_handle = self._ffi_handle.handle + + resp = FfiClient.instance.request(req) + return bool(resp.local_data_track_is_published.is_published) + + async def unpublish(self) -> None: + """Unpublishes the track.""" + req = proto_ffi.FfiRequest() + req.local_data_track_unpublish.track_handle = self._ffi_handle.handle + FfiClient.instance.request(req) + + def __repr__(self) -> str: + return f"rtc.LocalDataTrack(sid={self._info.sid}, name={self._info.name})" + + +class RemoteDataTrack: + """Data track published by a remote participant.""" + + def __init__(self, owned_info: proto_data_track.OwnedRemoteDataTrack) -> None: + self._info = DataTrackInfo( + sid=owned_info.info.sid, + name=owned_info.info.name, + uses_e2ee=owned_info.info.uses_e2ee, + ) + self._ffi_handle = FfiHandle(owned_info.handle.id) + self._publisher_identity = owned_info.publisher_identity + + @property + def info(self) -> DataTrackInfo: + """Information about the data track.""" + return self._info + + @property + def publisher_identity(self) -> str: + """Identity of the participant who published the track.""" + return self._publisher_identity + + def subscribe(self, *, buffer_size: Optional[int] = None) -> DataTrackStream: + """Subscribes to the data track to receive frames. + + Args: + buffer_size: Maximum number of received frames to buffer internally. + When ``None``, the default buffer size is used. + Zero is not a valid buffer size; if a value of zero is provided, it will be clamped to one. + + Returns a :class:`DataTrackStream` that yields + :class:`DataTrackFrame` instances as they arrive. If the + subscription encounters an error, it is raised as + :class:`SubscribeDataTrackError` when iteration ends. + """ + opts = proto_data_track.DataTrackSubscribeOptions() + if buffer_size is not None: + opts.buffer_size = buffer_size + + req = proto_ffi.FfiRequest() + req.subscribe_data_track.track_handle = self._ffi_handle.handle + req.subscribe_data_track.options.CopyFrom(opts) + + resp = FfiClient.instance.request(req) + return DataTrackStream(resp.subscribe_data_track.stream) + + def is_published(self) -> bool: + """Whether or not the track is still published.""" + req = proto_ffi.FfiRequest() + req.remote_data_track_is_published.track_handle = self._ffi_handle.handle + + resp = FfiClient.instance.request(req) + return bool(resp.remote_data_track_is_published.is_published) + + def set_pipeline_options(self, *, max_partial_frames: Optional[int] = None) -> None: + """Configures options for the pipeline handling incoming packets for this track. + + These options apply to all current and future subscriptions of this track, and + may be set at any time. New options take effect with the next received packet. + + Args: + max_partial_frames: Maximum number of partial frames the depacketizer will + track concurrently for this track. Defaults to 1. Higher values give more + out-of-order tolerance for high-frequency senders at the cost of additional + buffering. Zero is not a valid value; if a value of zero is provided, it + will be clamped to one. When ``None``, the current value is kept. + """ + opts = proto_data_track.RemoteDataTrackPipelineOptions() + if max_partial_frames is not None: + opts.max_partial_frames = max_partial_frames + + req = proto_ffi.FfiRequest() + req.remote_data_track_set_pipeline_options.track_handle = self._ffi_handle.handle + req.remote_data_track_set_pipeline_options.options.CopyFrom(opts) + + FfiClient.instance.request(req) + + def __repr__(self) -> str: + return ( + f"rtc.RemoteDataTrack(sid={self._info.sid}, name={self._info.name}, " + f"publisher_identity={self._publisher_identity})" + ) + + +class DataTrackStream: + """An active subscription to a remote data track. + + Use as an async iterator to receive frames:: + + stream = remote_track.subscribe() + async for frame in stream: + process(frame.payload) + + Dropping or closing the stream unsubscribes from the track. + + If subscribing to the track fails, :class:`SubscribeDataTrackError` + is raised when iteration ends instead of a normal ``StopAsyncIteration``. + """ + + def __init__(self, owned_info: proto_data_track.OwnedDataTrackStream) -> None: + self._ffi_handle = FfiHandle(owned_info.handle.id) + handle_id = owned_info.handle.id + + self._queue = FfiClient.instance.queue.subscribe( + filter_fn=lambda e: ( + e.WhichOneof("message") == "data_track_stream_event" + and e.data_track_stream_event.stream_handle == handle_id + ), + ) + self._closed = False + + async def read(self) -> Optional[DataTrackFrame]: + """Read a single frame, or ``None`` if the stream has ended.""" + try: + return await self.__anext__() + except StopAsyncIteration: + return None + + def __aiter__(self) -> AsyncIterator[DataTrackFrame]: + return self + + async def __anext__(self) -> DataTrackFrame: + if self._closed: + raise StopAsyncIteration + + self._send_read_request() + event: proto_ffi.FfiEvent = await self._queue.get() + stream_event = event.data_track_stream_event + detail = stream_event.WhichOneof("detail") + + if detail == "frame_received": + proto_frame = stream_event.frame_received.frame + user_ts: Optional[int] = None + if proto_frame.HasField("user_timestamp"): + user_ts = proto_frame.user_timestamp + return DataTrackFrame( + payload=proto_frame.payload, + user_timestamp=user_ts, + ) + elif detail == "eos": + self._close() + if stream_event.eos.HasField("error"): + raise SubscribeDataTrackError(stream_event.eos.error.message) + raise StopAsyncIteration + else: + self._close() + raise StopAsyncIteration + + def _send_read_request(self) -> None: + req = proto_ffi.FfiRequest() + req.data_track_stream_read.stream_handle = self._ffi_handle.handle + FfiClient.instance.request(req) + + def _close(self) -> None: + if not self._closed: + self._closed = True + FfiClient.instance.queue.unsubscribe(self._queue) + + def close(self) -> None: + """Explicitly close the subscription and unsubscribe.""" + self._close() + self._ffi_handle.dispose() + + async def aclose(self) -> None: + self.close() diff --git a/livekit-rtc/livekit/rtc/e2ee.py b/livekit-rtc/livekit/rtc/e2ee.py index 3b296bd0..8306658e 100644 --- a/livekit-rtc/livekit/rtc/e2ee.py +++ b/livekit-rtc/livekit/rtc/e2ee.py @@ -13,7 +13,7 @@ # limitations under the License. from dataclasses import dataclass, field -from typing import List, Optional +from typing import List, Optional, cast from ._ffi_client import FfiClient from ._proto import e2ee_pb2 as proto_e2ee @@ -22,6 +22,7 @@ DEFAULT_RATCHET_SALT = b"LKFrameEncryptionKey" DEFAULT_RATCHET_WINDOW_SIZE = 16 DEFAULT_FAILURE_TOLERANCE = -1 +DEFAULT_KEY_RING_SIZE = 16 @dataclass @@ -30,6 +31,10 @@ class KeyProviderOptions: ratchet_salt: bytes = DEFAULT_RATCHET_SALT ratchet_window_size: int = DEFAULT_RATCHET_WINDOW_SIZE failure_tolerance: int = DEFAULT_FAILURE_TOLERANCE + key_ring_size: int = DEFAULT_KEY_RING_SIZE + key_derivation_function: proto_e2ee.KeyDerivationFunction.ValueType = ( + proto_e2ee.KeyDerivationFunction.PBKDF2 + ) @dataclass @@ -48,6 +53,17 @@ def options(self) -> KeyProviderOptions: return self._options def set_shared_key(self, key: bytes, key_index: int) -> None: + """Sets the shared encryption key. + + Parameters: + key (bytes): The new shared key. + key_index (int): The index of the key. + + Example: + ```python + key_provider.set_shared_key(b"my_shared_key", key_index=1) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.set_shared_key.key_index = key_index @@ -55,14 +71,40 @@ def set_shared_key(self, key: bytes, key_index: int) -> None: FfiClient.instance.request(req) def export_shared_key(self, key_index: int) -> bytes: + """Exports the shared encryption key. + + Parameters: + key_index (int): The index of the key to export. + + Returns: + bytes: The exported shared key. + + Example: + ```python + key = key_provider.export_shared_key(key_index=1) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.get_shared_key.key_index = key_index resp = FfiClient.instance.request(req) key = resp.e2ee.get_shared_key.key - return key + return cast(bytes, key) def ratchet_shared_key(self, key_index: int) -> bytes: + """Ratchets the shared encryption key to a new key. + + Parameters: + key_index (int): The index of the key to ratchet. + + Returns: + bytes: The new ratcheted shared key. + + Example: + ```python + new_key = key_provider.ratchet_shared_key(key_index=1) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.ratchet_shared_key.key_index = key_index @@ -70,9 +112,21 @@ def ratchet_shared_key(self, key_index: int) -> bytes: resp = FfiClient.instance.request(req) new_key = resp.e2ee.ratchet_shared_key.new_key - return new_key + return cast(bytes, new_key) def set_key(self, participant_identity: str, key: bytes, key_index: int) -> None: + """Sets the encryption key for a specific participant. + + Parameters: + participant_identity (str): The identity of the participant. + key (bytes): The encryption key to set. + key_index (int): The index of the key. + + Example: + ```python + key_provider.set_key("participant123", b"participant_key", key_index=2) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.set_key.participant_identity = participant_identity @@ -83,15 +137,43 @@ def set_key(self, participant_identity: str, key: bytes, key_index: int) -> None FfiClient.instance.request(req) def export_key(self, participant_identity: str, key_index: int) -> bytes: + """Exports the encryption key for a specific participant. + + Parameters: + participant_identity (str): The identity of the participant. + key_index (int): The index of the key to export. + + Returns: + bytes: The exported key. + + Example: + ```python + key = key_provider.export_key("participant123", key_index=2) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.get_key.participant_identity = participant_identity req.e2ee.get_key.key_index = key_index resp = FfiClient.instance.request(req) key = resp.e2ee.get_key.key - return key + return cast(bytes, key) def ratchet_key(self, participant_identity: str, key_index: int) -> bytes: + """Ratchets the encryption key for a specific participant to a new key. + + Parameters: + participant_identity (str): The identity of the participant. + key_index (int): The index of the key to ratchet. + + Returns: + bytes: The new ratcheted key. + + Example: + ```python + new_key = key_provider.ratchet_key("participant123", key_index=2) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.ratchet_key.participant_identity = participant_identity @@ -99,22 +181,32 @@ def ratchet_key(self, participant_identity: str, key_index: int) -> bytes: resp = FfiClient.instance.request(req) new_key = resp.e2ee.ratchet_key.new_key - return new_key + return cast(bytes, new_key) class FrameCryptor: def __init__( - self, room_handle: int, participant_identity: str, key_index: int, enabled: bool + self, + room_handle: int, + participant_identity: str, + track_sid: str, + key_index: int, + enabled: bool, ): self._room_handle = room_handle self._enabled = enabled self._participant_identity = participant_identity + self._track_sid = track_sid self._key_index = key_index @property def participant_identity(self) -> str: return self._participant_identity + @property + def track_sid(self) -> str: + return self._track_sid + @property def key_index(self) -> int: return self._key_index @@ -124,18 +216,40 @@ def enabled(self) -> bool: return self._enabled def set_enabled(self, enabled: bool) -> None: + """Enables or disables frame encryption. + + Parameters: + enabled (bool): True to enable, False to disable. + + Example: + ```python + frame_cryptor.set_enabled(True) + ``` + """ self._enabled = enabled req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.cryptor_set_enabled.participant_identity = self._participant_identity + req.e2ee.cryptor_set_enabled.track_sid = self._track_sid req.e2ee.cryptor_set_enabled.enabled = enabled FfiClient.instance.request(req) def set_key_index(self, key_index: int) -> None: + """Sets the key index for encryption/decryption. + + Parameters: + key_index (int): The new key index. + + Example: + ```python + frame_cryptor.set_key_index(3) + ``` + """ self._key_index = key_index req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle req.e2ee.cryptor_set_key_index.participant_identity = self._participant_identity + req.e2ee.cryptor_set_key_index.track_sid = self._track_sid req.e2ee.cryptor_set_key_index.key_index = key_index FfiClient.instance.request(req) @@ -147,9 +261,7 @@ def __init__(self, room_handle: int, options: Optional[E2EEOptions]): self._enabled = options is not None if options is not None: - self._key_provider = KeyProvider( - self._room_handle, options.key_provider_options - ) + self._key_provider = KeyProvider(self._room_handle, options.key_provider_options) @property def key_provider(self) -> Optional[KeyProvider]: @@ -160,6 +272,16 @@ def enabled(self) -> bool: return self._enabled def set_enabled(self, enabled: bool) -> None: + """Enables or disables end-to-end encryption. + + Parameters: + enabled (bool): True to enable, False to disable. + + Example: + ```python + e2ee_manager.set_enabled(True) + ``` + """ self._enabled = enabled req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle @@ -167,8 +289,21 @@ def set_enabled(self, enabled: bool) -> None: FfiClient.instance.request(req) def frame_cryptors(self) -> List[FrameCryptor]: + """Retrieves the list of frame cryptors for participants. + + Returns: + List[FrameCryptor]: A list of FrameCryptor instances. + + Example: + ```python + cryptors = e2ee_manager.frame_cryptors() + for cryptor in cryptors: + print(cryptor.participant_identity) + ``` + """ req = proto_ffi.FfiRequest() req.e2ee.room_handle = self._room_handle + req.e2ee.manager_get_frame_cryptors.SetInParent() resp = FfiClient.instance.request(req) frame_cryptors = [] @@ -177,6 +312,7 @@ def frame_cryptors(self) -> List[FrameCryptor]: FrameCryptor( self._room_handle, frame_cryptor.participant_identity, + frame_cryptor.track_sid, frame_cryptor.key_index, frame_cryptor.enabled, ) diff --git a/livekit-rtc/livekit/rtc/event_emitter.py b/livekit-rtc/livekit/rtc/event_emitter.py new file mode 100644 index 00000000..bc64d1d7 --- /dev/null +++ b/livekit-rtc/livekit/rtc/event_emitter.py @@ -0,0 +1,200 @@ +import inspect +import asyncio +from typing import Any, Callable, Dict, Set, Optional, Generic, TypeVar + +from .log import logger + +T_contra = TypeVar("T_contra", contravariant=True) + + +class EventEmitter(Generic[T_contra]): + def __init__(self) -> None: + """ + Initialize a new instance of EventEmitter. + """ + self._events: Dict[T_contra, Set[Callable]] = dict() + + def emit(self, event: T_contra, *args: Any) -> None: + """ + Trigger all callbacks associated with the given event. + + Args: + event (T): The event to emit. + *args: Positional arguments to pass to the callbacks. + + Example: + Basic usage of emit: + + ```python + emitter = EventEmitter[str]() + + def greet(name): + print(f"Hello, {name}!") + + emitter.on('greet', greet) + emitter.emit('greet', 'Alice') # Output: Hello, Alice! + ``` + """ + if event in self._events: + callables = self._events[event].copy() + for callback in callables: + try: + sig = inspect.signature(callback) + params = sig.parameters.values() + + has_varargs = any(p.kind == p.VAR_POSITIONAL for p in params) + if has_varargs: + callback(*args) + else: + positional_params = [ + p + for p in params + if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD) + ] + num_params = len(positional_params) + num_args = min(len(args), num_params) + callback_args = args[:num_args] + + callback(*callback_args) + except TypeError: + raise + except Exception: + logger.exception(f"failed to emit event {event}") + + def once(self, event: T_contra, callback: Optional[Callable] = None) -> Callable: + """ + Register a callback to be called only once when the event is emitted. + + If a callback is provided, it registers the callback directly. + If no callback is provided, it returns a decorator for use with function definitions. + + Args: + event (T): The event to listen for. + callback (Callable, optional): The callback to register. Defaults to None. + + Returns: + Callable: The registered callback or a decorator if callback is None. + + Example: + Using once with a direct callback: + + ```python + emitter = EventEmitter[str]() + + def greet_once(name): + print(f"Hello once, {name}!") + + emitter.once('greet', greet_once) + emitter.emit('greet', 'Bob') # Output: Hello once, Bob! + emitter.emit('greet', 'Bob') # No output, callback was removed after first call + ``` + + Using once as a decorator: + + ```python + emitter = EventEmitter[str]() + + @emitter.once('greet') + def greet_once(name): + print(f"Hello once, {name}!") + + emitter.emit('greet', 'Bob') # Output: Hello once, Bob! + emitter.emit('greet', 'Bob') # No output + ``` + """ + if callback is not None: + + def once_callback(*args: Any, **kwargs: Any) -> None: + self.off(event, once_callback) + callback(*args, **kwargs) + + return self.on(event, once_callback) + else: + + def decorator(callback: Callable) -> Callable: + self.once(event, callback) + return callback + + return decorator + + def on(self, event: T_contra, callback: Optional[Callable] = None) -> Callable: + """ + Register a callback to be called whenever the event is emitted. + + If a callback is provided, it registers the callback directly. + If no callback is provided, it returns a decorator for use with function definitions. + + Args: + event (T): The event to listen for. + callback (Callable, optional): The callback to register. Defaults to None. + + Returns: + Callable: The registered callback or a decorator if callback is None. + + Example: + Using on with a direct callback: + + ```python + emitter = EventEmitter[str]() + + def greet(name): + print(f"Hello, {name}!") + + emitter.on('greet', greet) + emitter.emit('greet', 'Charlie') # Output: Hello, Charlie! + ``` + + Using on as a decorator: + + ```python + emitter = EventEmitter[str]() + + @emitter.on('greet') + def greet(name): + print(f"Hello, {name}!") + + emitter.emit('greet', 'Charlie') # Output: Hello, Charlie! + ``` + """ + if callback is not None: + if asyncio.iscoroutinefunction(callback): + raise ValueError( + "Cannot register an async callback with `.on()`. Use `asyncio.create_task` within your synchronous callback instead." + ) + + if event not in self._events: + self._events[event] = set() + self._events[event].add(callback) + return callback + else: + + def decorator(callback: Callable) -> Callable: + self.on(event, callback) + return callback + + return decorator + + def off(self, event: T_contra, callback: Callable) -> None: + """ + Unregister a callback from an event. + + Args: + event (T): The event to stop listening to. + callback (Callable): The callback to remove. + + Example: + Removing a callback: + + ```python + emitter = EventEmitter[str]() + + def greet(name): + print(f"Hello, {name}!") + + emitter.on('greet', greet) + emitter.off('greet', greet) + emitter.emit('greet', 'Dave') # No output, callback was removed + ``` + """ + if event in self._events: + self._events[event].discard(callback) diff --git a/livekit-rtc/livekit/rtc/frame_processor.py b/livekit-rtc/livekit/rtc/frame_processor.py new file mode 100644 index 00000000..ae03d537 --- /dev/null +++ b/livekit-rtc/livekit/rtc/frame_processor.py @@ -0,0 +1,37 @@ +from abc import ABC, abstractmethod +from typing import Generic, TypeVar, Union +from .audio_frame import AudioFrame +from .video_frame import VideoFrame + + +T = TypeVar("T", bound=Union[AudioFrame, VideoFrame]) + + +class FrameProcessor(Generic[T], ABC): + @property + @abstractmethod + def enabled(self) -> bool: ... + + @enabled.setter + @abstractmethod + def enabled(self, value: bool) -> None: ... + + def _on_stream_info_updated( + self, + *, + room_name: str, + participant_identity: str, + publication_sid: str, + ) -> None: ... + + def _on_stream_info_cleared(self) -> None: ... + + def _on_credentials_updated(self, *, token: str, url: str) -> None: ... + + def _on_credentials_cleared(self) -> None: ... + + @abstractmethod + def _process(self, frame: T) -> T: ... + + @abstractmethod + def _close(self) -> None: ... diff --git a/livekit-rtc/livekit/rtc/jupyter.py b/livekit-rtc/livekit/rtc/jupyter.py new file mode 100644 index 00000000..8a2a2367 --- /dev/null +++ b/livekit-rtc/livekit/rtc/jupyter.py @@ -0,0 +1,68 @@ +from __future__ import annotations + +import sys +import atexit +import contextlib +import html +from IPython.core.display import HTML +from IPython.display import display +from importlib.resources import as_file, files + +_resource_stack = contextlib.ExitStack() +atexit.register(_resource_stack.close) + + +def room_html(url: str, token: str, *, width: str, height: str) -> HTML: + """ + Generate the HTML needed to embed a LiveKit room. + + Args: + url (str): The LiveKit room URL. + token (str): The LiveKit join token. + + Important: + The returned HTML contains the provided `url` and `token` values directly. + Avoid using sensitive tokens in public notebooks (e.g., tokens with long expiration times). + """ + token_placeholder = "##livekit-token-placeholder##" + url_placeholder = "##livekit-url-placeholder##" + + index_path = files("livekit.rtc.resources") / "jupyter-html" / "index.html" + index_path = _resource_stack.enter_context(as_file(index_path)) + + # turns out that directly replacing the URL/token is necessary, as Colab or Jupyter comms become + # unreliable when the main thread is busy/blocked. + # it also avoid the need to use --expose-app-in-browser when starting jupyter notebook + html_text = index_path.read_text() + html_text = html_text.replace(token_placeholder, token) + html_text = html_text.replace(url_placeholder, url) + + IN_COLAB = "google.colab" in sys.modules + + # Colab output already runs inside an iframe, so we don’t need to create an additional one. + # It also injects code into this iframe to handle microphone usage, so we need to preserve it. + if not IN_COLAB: + escaped_content = html.escape(html_text, quote=True) + # the extra space in the iframe_html is to avoid IPython suggesting to use IFrame instead. + # it isn't possible in our case, as we need to use srcdoc. + html_text = ( + f' ' + ) + + return HTML(html_text) # type: ignore[no-untyped-call] + + +def display_room(url: str, token: str, *, width: str = "100%", height: str = "110px") -> None: + """ + Display a LiveKit room in a Jupyter notebook or Google Colab. + + Args: + url (str): The LiveKit room URL. + token (str): The LiveKit join token. + + Important: + The rendered HTML will include the provided `url` and `token` in plain text. + Avoid using sensitive tokens in public notebooks (e.g., tokens with long expiration times). + """ + display(room_html(url, token, width=width, height=height)) # type: ignore[no-untyped-call] diff --git a/livekit-rtc/livekit/rtc/log.py b/livekit-rtc/livekit/rtc/log.py new file mode 100644 index 00000000..72c505f8 --- /dev/null +++ b/livekit-rtc/livekit/rtc/log.py @@ -0,0 +1,3 @@ +import logging + +logger = logging.getLogger("livekit") diff --git a/livekit-rtc/livekit/rtc/media_devices.py b/livekit-rtc/livekit/rtc/media_devices.py new file mode 100644 index 00000000..4567e660 --- /dev/null +++ b/livekit-rtc/livekit/rtc/media_devices.py @@ -0,0 +1,596 @@ +# Copyright 2025 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +import asyncio +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, AsyncIterator, Optional + +import numpy as np +import threading + +if TYPE_CHECKING: + import sounddevice as sd # type: ignore[import-not-found] + +from . import AudioSource +from .audio_frame import AudioFrame +from .apm import AudioProcessingModule +from .audio_mixer import AudioMixer +from .audio_stream import AudioStream +from .track import Track + +""" +Media device helpers built on top of the `sounddevice` library. + +This module provides a small, Pythonic helper around native audio I/O for +LiveKit RTC usage: + +- Capture the default audio input device and feed frames into `rtc.AudioSource`. +- Optionally enable audio processing via `rtc.AudioProcessingModule` (AEC, + noise suppression, high-pass filter, AGC). Frames are processed in 10 ms + chunks as required by APM. +- Play arbitrary audio frames to the default speaker. When AEC is enabled on + the input, the `OutputPlayer` can feed the APM reverse stream so echo + cancellation has access to render (speaker) audio. + +Notes on AEC wiring: +- AEC requires feeding both capture (mic) and reverse (speaker) paths into + the same APM instance. This module does not automatically capture output from + other players. To enable AEC, the output player feeds APM's reverse stream + and we set stream delays derived from PortAudio timing. +""" + + +DEFAULT_SAMPLE_RATE = 48000 +DEFAULT_CHANNELS = 1 +FRAME_SAMPLES = 480 # 10 ms at 48 kHz +BLOCKSIZE = 4800 # 100 ms I/O buffer size for sounddevice + + +class _AudioStreamIterator: + """Adapter to convert AudioStream (AsyncIterator[AudioFrameEvent]) to AsyncIterator[AudioFrame]. + + This adapter wraps an AudioStream and extracts the frame from each AudioFrameEvent, + making it compatible with AudioMixer which expects AsyncIterator[AudioFrame]. + """ + + def __init__(self, audio_stream: AudioStream) -> None: + self._audio_stream = audio_stream + + def __aiter__(self) -> AsyncIterator[AudioFrame]: + return self + + async def __anext__(self) -> AudioFrame: + event = await self._audio_stream.__anext__() + return event.frame + + async def aclose(self) -> None: + await self._audio_stream.aclose() + + +def _ensure_loop(loop: Optional[asyncio.AbstractEventLoop]) -> asyncio.AbstractEventLoop: + return loop or asyncio.get_event_loop() + + +class _APMDelayEstimator: + """Thread-safe store for last known output (render) delay in seconds. + + The sounddevice callbacks are invoked on PortAudio's threads. This helper allows + sharing the latest output delay measurement with the input callback so we can set + APM's combined stream delay (render + capture). + """ + + def __init__(self) -> None: + self._lock = threading.Lock() + self._output_delay_sec: float = 0.0 + + def set_output_delay(self, delay_sec: float) -> None: + with self._lock: + self._output_delay_sec = float(delay_sec) + + def get_output_delay(self) -> float: + with self._lock: + return self._output_delay_sec + + +@dataclass +class InputCapture: + """Holds resources for an active audio input capture. + + Attributes: + source: `rtc.AudioSource` that receives captured frames. This can be + published as a `LocalAudioTrack`. + input_stream: Underlying `sounddevice.InputStream`. + task: Async task that drains a queue and calls `source.capture_frame`. + apm: Optional `rtc.AudioProcessingModule` used to process 10 ms frames + (AEC, NS, HPF, AGC). When performing echo cancellation, pass this + instance to `open_output_player` so reverse frames are provided. + delay_estimator: Internal helper used to combine capture and render delays. + """ + + source: AudioSource + input_stream: "sd.InputStream" + task: asyncio.Task + apm: Optional[AudioProcessingModule] + delay_estimator: Optional[_APMDelayEstimator] + + async def aclose(self) -> None: + """Stop capture and close underlying resources.""" + if self.task and not self.task.done(): + self.task.cancel() + try: + await self.task + except asyncio.CancelledError: + pass + try: + self.input_stream.stop() + self.input_stream.close() + except Exception: + pass + + +class OutputPlayer: + """Simple audio output helper using `sounddevice.OutputStream`. + + When `apm_for_reverse` is provided, this player will feed the same PCM it + renders (in 10 ms frames) into the APM reverse path so that echo + cancellation can correlate mic input with speaker output. + + The OutputPlayer includes an internal `AudioMixer` for convenient multi-track + playback. Use `add_track()` and `remove_track()` to dynamically manage tracks, + then call `start()` to begin playback. + """ + + def __init__( + self, + *, + sample_rate: int = DEFAULT_SAMPLE_RATE, + num_channels: int = DEFAULT_CHANNELS, + blocksize: int = BLOCKSIZE, + apm_for_reverse: Optional[AudioProcessingModule] = None, + output_device: Optional[int] = None, + delay_estimator: Optional[_APMDelayEstimator] = None, + ) -> None: + import sounddevice as sd + + self._sample_rate = sample_rate + self._num_channels = num_channels + self._blocksize = blocksize + self._apm = apm_for_reverse + self._buffer = bytearray() + self._buffer_lock = asyncio.Lock() + self._play_task: Optional[asyncio.Task] = None + self._running = False + self._delay_estimator = delay_estimator + + # Internal mixer for add_track/remove_track API + self._mixer: Optional[AudioMixer] = None + self._track_streams: dict[ + str, tuple[AudioStream, _AudioStreamIterator] + ] = {} # track.sid -> (AudioStream, adapter) + + def _callback(outdata: np.ndarray, frame_count: int, time_info: Any, status: Any) -> None: + # Pull PCM int16 from buffer; zero if not enough + bytes_needed = frame_count * 2 + # Important: Do not take asyncio locks in realtime callbacks. We keep the + # critical section minimal and tolerate occasional underruns. + available = len(self._buffer) + if available >= bytes_needed: + chunk = self._buffer[:bytes_needed] + outdata[:, 0] = np.frombuffer(chunk, dtype=np.int16, count=frame_count) + del self._buffer[:bytes_needed] + elif available > 0: + outdata[: available // 2, 0] = np.frombuffer( + self._buffer[:available], dtype=np.int16, count=available // 2 + ) + outdata[available // 2 :, 0] = 0 + del self._buffer[:available] + else: + outdata.fill(0) + + # Measure render (output) delay: time until DAC from current callback time + try: + output_delay_sec = float(time_info.outputBufferDacTime - time_info.currentTime) + if self._delay_estimator is not None: + self._delay_estimator.set_output_delay(output_delay_sec) + except Exception: + pass + + if self._apm is not None: + # Feed reverse stream in 10 ms frames for AEC + num_chunks = frame_count // FRAME_SAMPLES + for i in range(num_chunks): + start = i * FRAME_SAMPLES + end = start + FRAME_SAMPLES + if end > frame_count: + break + render_chunk = outdata[start:end, 0] + render_frame = AudioFrame( + render_chunk.tobytes(), self._sample_rate, 1, FRAME_SAMPLES + ) + try: + self._apm.process_reverse_stream(render_frame) + except Exception: + # Ignore reverse stream errors in callback + pass + + self._stream = sd.OutputStream( + callback=_callback, + dtype="int16", + channels=num_channels, + device=output_device, + samplerate=sample_rate, + blocksize=blocksize, + ) + + async def add_track(self, track: Track) -> None: + """Add an audio track to the internal mixer for playback. + + This creates an `AudioStream` from the track and adds it to the internal + mixer. The mixer is created lazily on first track addition. Call `start()` + to begin playback of all added tracks. + + Args: + track: The audio track to add (typically from a remote participant). + + Raises: + ValueError: If the track is not an audio track or has already been added. + """ + if track.sid in self._track_streams: + raise ValueError(f"Track {track.sid} already added to player") + + # Create mixer on first track addition + if self._mixer is None: + self._mixer = AudioMixer(sample_rate=self._sample_rate, num_channels=self._num_channels) + + # Create audio stream for this track + stream = AudioStream(track, sample_rate=self._sample_rate, num_channels=self._num_channels) + # Wrap the stream with an adapter to convert AudioFrameEvent to AudioFrame + stream_iterator = _AudioStreamIterator(stream) + + self._track_streams[track.sid] = (stream, stream_iterator) + self._mixer.add_stream(stream_iterator) + + async def remove_track(self, track: Track) -> None: + """Remove an audio track from the internal mixer. + + This removes the track's stream from the mixer and closes it. + + Args: + track: The audio track to remove. + """ + entry = self._track_streams.pop(track.sid, None) + if entry is None: + return + + stream, stream_iterator = entry + if self._mixer is not None: + try: + self._mixer.remove_stream(stream_iterator) + except Exception: + pass + + try: + await stream.aclose() + except Exception: + pass + + async def start(self) -> None: + """Start playback of all tracks in the internal mixer. + + This begins a background task that consumes frames from the internal mixer + and sends them to the output device. Tracks can be added or removed + dynamically using `add_track()` and `remove_track()`. + + Raises: + RuntimeError: If playback is already started or no mixer is available. + """ + if self._play_task is not None and not self._play_task.done(): + raise RuntimeError("Playback already started") + + if self._mixer is None: + self._mixer = AudioMixer(sample_rate=self._sample_rate, num_channels=self._num_channels) + + async def _playback_loop() -> None: + """Internal playback loop that consumes frames from the mixer.""" + self._running = True + self._stream.start() + try: + if self._mixer is not None: + async for frame in self._mixer: + if not self._running: + break + # Append raw PCM bytes for callback consumption + self._buffer.extend(frame.data.tobytes()) + finally: + self._running = False + try: + self._stream.stop() + self._stream.close() + except Exception: + pass + + self._play_task = asyncio.create_task(_playback_loop()) + + async def aclose(self) -> None: + """Stop playback and close the output stream. + + This also cleans up all added tracks and the internal mixer. + """ + self._running = False + + # Cancel playback task if running + if self._play_task is not None and not self._play_task.done(): + self._play_task.cancel() + try: + await self._play_task + except asyncio.CancelledError: + pass + + # Clean up all track streams + for stream, _ in list(self._track_streams.values()): + try: + await stream.aclose() + except Exception: + pass + self._track_streams.clear() + + # Close mixer + if self._mixer is not None: + try: + await self._mixer.aclose() + except Exception: + pass + self._mixer = None + + # Close output stream + try: + self._stream.stop() + self._stream.close() + except Exception: + pass + + +class MediaDevices: + """High-level interface to native audio devices. + + - Device enumeration helpers. + - Audio input capture into `rtc.AudioSource` with optional APM processing. + - Audio output player that can feed APM reverse stream for AEC. + + Design notes: + - APM operates on 10 ms frames; this module slices input/output audio into + `FRAME_SAMPLES` for processing calls. + - For AEC to be effective, render audio that could leak back into the mic + should be played through `OutputPlayer` with the same `apm` instance. + - Timing alignment: this helper does not attempt to set device latency on + APM; for most setups the default behavior is acceptable. + """ + + def __init__( + self, + *, + loop: Optional[asyncio.AbstractEventLoop] = None, + input_sample_rate: int = DEFAULT_SAMPLE_RATE, + output_sample_rate: int = DEFAULT_SAMPLE_RATE, + num_channels: int = DEFAULT_CHANNELS, + blocksize: int = BLOCKSIZE, + ) -> None: + self._loop = _ensure_loop(loop) + self._in_sr = input_sample_rate + self._out_sr = output_sample_rate + self._channels = num_channels + self._blocksize = blocksize + self._delay_estimator: Optional[_APMDelayEstimator] = None + self._apm: Optional[AudioProcessingModule] = None + + # Device enumeration + def list_input_devices(self) -> list[dict[str, Any]]: + """List available input devices. + + Returns a list of dictionaries with the `sounddevice` metadata and an + added `index` key corresponding to the device index. + """ + import sounddevice as sd + + devices = sd.query_devices() + result: list[dict[str, Any]] = [] + for idx, dev in enumerate(devices): + if dev.get("max_input_channels", 0) > 0: + result.append({"index": idx, **dev}) + return result + + def list_output_devices(self) -> list[dict[str, Any]]: + """List available output devices with indices.""" + import sounddevice as sd + + devices = sd.query_devices() + result: list[dict[str, Any]] = [] + for idx, dev in enumerate(devices): + if dev.get("max_output_channels", 0) > 0: + result.append({"index": idx, **dev}) + return result + + def default_input_device(self) -> Optional[int]: + """Return the default input device index (or None).""" + import sounddevice as sd + + dev = sd.default.device + return dev[0] if isinstance(dev, (list, tuple)) else None + + def default_output_device(self) -> Optional[int]: + """Return the default output device index (or None).""" + import sounddevice as sd + + dev = sd.default.device + return dev[1] if isinstance(dev, (list, tuple)) else None + + # Capture / Playback + def open_input( + self, + *, + enable_aec: bool = True, + noise_suppression: bool = True, + high_pass_filter: bool = True, + auto_gain_control: bool = True, + input_device: Optional[int] = None, + queue_capacity: int = 50, + input_channel_index: Optional[int] = None, + ) -> InputCapture: + """Open the default (or chosen) audio input device and start capture. + + Frames are sliced into 10 ms chunks. If any processing option is enabled, + an `AudioProcessingModule` is created and applied to each frame before it + is queued for `AudioSource.capture_frame`. + + To enable AEC end-to-end, call `open_output()` after opening the input + device. The output player will automatically use the input's APM for + reverse stream processing, enabling echo cancellation. + + Args: + enable_aec: Enable acoustic echo cancellation. + noise_suppression: Enable noise suppression. + high_pass_filter: Enable high-pass filtering. + auto_gain_control: Enable automatic gain control. + input_device: Optional input device index (default system device if None). + queue_capacity: Max queued frames between callback and async pump. + input_channel_index: Optional zero-based device channel to capture. If provided, + only that channel is opened (via sounddevice mapping) and used as mono input. + + Returns: + InputCapture: Holder with `source`, `apm`, and `aclose()`. + """ + import sounddevice as sd + + loop = self._loop + source = AudioSource(self._in_sr, self._channels, loop=loop) + apm: Optional[AudioProcessingModule] = None + if enable_aec or noise_suppression or high_pass_filter or auto_gain_control: + apm = AudioProcessingModule( + echo_cancellation=enable_aec, + noise_suppression=noise_suppression, + high_pass_filter=high_pass_filter, + auto_gain_control=auto_gain_control, + ) + delay_estimator: Optional[_APMDelayEstimator] = ( + _APMDelayEstimator() if apm is not None else None + ) + # Store the shared estimator and APM on the device helper so the output player can reuse them + self._delay_estimator = delay_estimator + self._apm = apm + + # Queue from callback to async task + q: asyncio.Queue[AudioFrame] = asyncio.Queue(maxsize=queue_capacity) + + def _input_callback( + indata: np.ndarray, frame_count: int, time_info: Any, status: Any + ) -> None: + # Slice into 10 ms frames, optionally APM, enqueue for async capture + # Compute input (capture) delay using PortAudio timing; combine with last + # measured output delay to provide APM stream delay in milliseconds. + if apm is not None: + try: + input_delay_sec = float(time_info.currentTime - time_info.inputBufferAdcTime) + output_delay_sec = ( + float(delay_estimator.get_output_delay()) if delay_estimator else 0.0 + ) + total_delay_ms = int(max((input_delay_sec + output_delay_sec) * 1000.0, 0.0)) + try: + apm.set_stream_delay_ms(total_delay_ms) + except Exception: + pass + except Exception: + pass + num_frames = frame_count // FRAME_SAMPLES + for i in range(num_frames): + start = i * FRAME_SAMPLES + end = start + FRAME_SAMPLES + if end > frame_count: + break + chunk = indata[start:end, 0] + frame = AudioFrame( + data=chunk.tobytes(), + samples_per_channel=FRAME_SAMPLES, + sample_rate=self._in_sr, + num_channels=self._channels, + ) + if apm is not None: + try: + apm.process_stream(frame) + except Exception: + # Continue even if APM processing fails + pass + try: + # Non-blocking: drop if full + if not q.full(): + loop.call_soon_threadsafe(q.put_nowait, frame) + except Exception: + pass + + # Note: input_channel_index is currently not used as sounddevice mapping + # parameter is not supported in all versions. + input_stream = sd.InputStream( + callback=_input_callback, + dtype="int16", + channels=self._channels, + device=input_device, + samplerate=self._in_sr, + blocksize=self._blocksize, + ) + input_stream.start() + + async def _pump() -> None: + # Drain queue into AudioSource + while True: + try: + frame = await q.get() + except asyncio.CancelledError: + break + try: + await source.capture_frame(frame) + except Exception: + # Ignore capture errors to keep the pump alive + pass + + task = asyncio.create_task(_pump()) + return InputCapture( + source=source, + input_stream=input_stream, + task=task, + apm=apm, + delay_estimator=delay_estimator, + ) + + def open_output( + self, + *, + output_device: Optional[int] = None, + ) -> OutputPlayer: + """Create an `OutputPlayer` for rendering and (optionally) AEC reverse. + + If an input device was opened with AEC enabled, the output player will + automatically feed the APM's reverse stream for echo cancellation. + + Args: + output_device: Optional output device index (default system device if None). + """ + return OutputPlayer( + sample_rate=self._out_sr, + num_channels=self._channels, + blocksize=self._blocksize, + apm_for_reverse=self._apm, + output_device=output_device, + delay_estimator=self._delay_estimator, + ) diff --git a/livekit-rtc/livekit/rtc/participant.py b/livekit-rtc/livekit/rtc/participant.py index 7d7351f9..2a53526e 100644 --- a/livekit-rtc/livekit/rtc/participant.py +++ b/livekit-rtc/livekit/rtc/participant.py @@ -15,18 +15,31 @@ from __future__ import annotations import ctypes -from typing import List, Union +import asyncio +import datetime +import enum +import os +import mimetypes +import aiofiles +from typing import List, Union, Callable, Dict, Awaitable, Optional, Mapping, cast, TypeVar +from abc import abstractmethod, ABC from ._ffi_client import FfiClient, FfiHandle from ._proto import ffi_pb2 as proto_ffi from ._proto import participant_pb2 as proto_participant from ._proto.room_pb2 import ( - DataPacketKind, TrackPublishOptions, ) +from ._proto.room_pb2 import ( + ConnectionQuality as ProtoConnectionQuality, + DegradationPreference as ProtoDegradationPreference, +) from ._proto.room_pb2 import ( TranscriptionSegment as ProtoTranscriptionSegment, ) +from ._proto.track_pb2 import ( + ParticipantTrackPermission, +) from ._utils import BroadcastQueue from .track import LocalTrack from .track_publication import ( @@ -35,6 +48,20 @@ TrackPublication, ) from .transcription import Transcription +from .rpc import RpcError +from ._proto.rpc_pb2 import RpcMethodInvocationResponseRequest +from .log import logger + +from .rpc import RpcInvocationData +from .data_stream import ( + TextStreamWriter, + TextStreamInfo, + ByteStreamWriter, + ByteStreamInfo, + STREAM_CHUNK_SIZE, +) +from .data_track import LocalDataTrack +from ._proto import data_track_pb2 as proto_data_track class PublishTrackError(Exception): @@ -52,16 +79,68 @@ def __init__(self, message: str) -> None: self.message = message +class PublishDTMFError(Exception): + def __init__(self, message: str) -> None: + self.message = message + + class PublishTranscriptionError(Exception): def __init__(self, message: str) -> None: self.message = message -class Participant: +class PublishDataTrackError(Exception): + def __init__(self, message: str) -> None: + self.message = message + + +class ConnectionQuality(enum.IntEnum): + """Connection quality reported for a participant.""" + + QUALITY_UNKNOWN = -1 + """No connection-quality update has been reported yet (no proto equivalent).""" + QUALITY_POOR = ProtoConnectionQuality.QUALITY_POOR + QUALITY_GOOD = ProtoConnectionQuality.QUALITY_GOOD + QUALITY_EXCELLENT = ProtoConnectionQuality.QUALITY_EXCELLENT + QUALITY_LOST = ProtoConnectionQuality.QUALITY_LOST + + +class DegradationPreference(enum.IntEnum): + """Controls how the encoder degrades quality when bandwidth is constrained.""" + + BALANCED = ProtoDegradationPreference.DEGRADATION_PREFERENCE_BALANCED + """Balance between framerate and resolution degradation.""" + MAINTAIN_FRAMERATE = ProtoDegradationPreference.DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE + """Degrade resolution to maintain framerate (prioritize smooth motion).""" + MAINTAIN_RESOLUTION = ProtoDegradationPreference.DEGRADATION_PREFERENCE_MAINTAIN_RESOLUTION + """Degrade framerate to maintain resolution (prioritize image clarity).""" + MAINTAIN_FRAMERATE_AND_RESOLUTION = ( + ProtoDegradationPreference.DEGRADATION_PREFERENCE_MAINTAIN_FRAMERATE_AND_RESOLUTION + ) + """Maintain both framerate and resolution. Frames may be dropped before encoding + if necessary to avoid overusing network and encoder resources.""" + + +def _connection_quality_from_proto(value: int) -> ConnectionQuality: + try: + return ConnectionQuality(value) + except ValueError: + return ConnectionQuality.QUALITY_UNKNOWN + + +class Participant(ABC): def __init__(self, owned_info: proto_participant.OwnedParticipant) -> None: self._info = owned_info.info self._ffi_handle = FfiHandle(owned_info.handle.id) - self.tracks: dict[str, TrackPublication] = {} + self._connection_quality = ConnectionQuality.QUALITY_UNKNOWN + + @property + @abstractmethod + def track_publications(self) -> Mapping[str, TrackPublication]: + """ + A dictionary of track publications associated with the participant. + """ + ... @property def sid(self) -> str: @@ -79,8 +158,84 @@ def identity(self) -> str: def metadata(self) -> str: return self._info.metadata + @property + def attributes(self) -> dict[str, str]: + """Custom attributes associated with the participant.""" + return dict(self._info.attributes) + + @property + def kind(self) -> proto_participant.ParticipantKind.ValueType: + """Participant's kind (e.g., regular participant, ingress, egress, sip, agent).""" + return self._info.kind + + @property + def kind_details(self) -> list[proto_participant.ParticipantKindDetail.ValueType]: + """Additional details refining the participant's kind (e.g., cloud agent, forwarded, whatsapp/twilio connector, rtsp bridge).""" + return list(self._info.kind_details) + + @property + def state(self) -> proto_participant.ParticipantState.ValueType: + """Participant's connection state (joining, joined, active, disconnected).""" + return self._info.state + + @property + def joined_at(self) -> datetime.datetime | None: + """Timestamp of when the participant joined the room, or None if not yet joined.""" + if self._info.joined_at == 0: + return None + return datetime.datetime.fromtimestamp( + self._info.joined_at / 1000, tz=datetime.timezone.utc + ) + + @property + def permissions(self) -> proto_participant.ParticipantPermission: + """The participant's permissions within the room.""" + return self._info.permission + + @property + def connection_quality(self) -> ConnectionQuality: + """The participant's most recently reported connection quality. + + Returns ``ConnectionQuality.QUALITY_UNKNOWN`` until the first + connection-quality update is received. Updated automatically whenever a + ``connection_quality_changed`` event fires for this participant. + """ + return self._connection_quality + + @property + def disconnect_reason( + self, + ) -> Optional[proto_participant.DisconnectReason.ValueType]: + """Reason for the participant's disconnection. + + Returns one of DisconnectReasons or None if the participant isn't disconnected. Common reasons are: + - CLIENT_INITIATED - the client initiated the disconnect + - DUPLICATE_IDENTITY - another participant with the same identity has joined the room + - SERVER_SHUTDOWN - the server instance is shutting down + - PARTICIPANT_REMOVED - RoomService.RemoveParticipant was called + - ROOM_DELETED - RoomService.DeleteRoom was called + - STATE_MISMATCH - the client is attempting to resume a session, but server is not aware of it + - JOIN_FAILURE - client was unable to connect fully + + When dialing a participant via SIP, you may see the following reasons: + - USER_UNAVAILABLE - SIP callee did not respond in time + - USER_REJECTED - SIP callee rejected the call (busy) + - SIP_TRUNK_FAILURE - SIP protocol failure or unexpected response + """ + if self._info.disconnect_reason == proto_participant.DisconnectReason.UNKNOWN_REASON: + return None + return self._info.disconnect_reason + + +RpcHandler = Callable[["RpcInvocationData"], Union[Awaitable[Optional[str]], Optional[str]]] +F = TypeVar( + "F", bound=Callable[[RpcInvocationData], Union[Awaitable[Optional[str]], Optional[str]]] +) + class LocalParticipant(Participant): + """Represents the local participant in a room.""" + def __init__( self, room_queue: BroadcastQueue[proto_ffi.FfiEvent], @@ -88,15 +243,36 @@ def __init__( ) -> None: super().__init__(owned_info) self._room_queue = room_queue - self.tracks: dict[str, LocalTrackPublication] = {} # type: ignore + self._track_publications: dict[str, LocalTrackPublication] = {} + self._rpc_handlers: Dict[str, RpcHandler] = {} + + @property + def track_publications(self) -> Mapping[str, LocalTrackPublication]: + """ + A dictionary of track publications associated with the participant. + """ + return self._track_publications async def publish_data( self, payload: Union[bytes, str], - kind: DataPacketKind.ValueType = DataPacketKind.KIND_RELIABLE, - destination_sids: List[Union[str, "RemoteParticipant"]] = [], + *, + reliable: bool = True, + destination_identities: List[str] = [], topic: str = "", ) -> None: + """ + Publish arbitrary data to the room. + + Args: + payload (Union[bytes, str]): The data to publish. + reliable (bool, optional): Whether to send reliably or not. Defaults to True. + destination_identities (List[str], optional): List of participant identities to send to. Defaults to []. + topic (str, optional): The topic under which to publish the data. Defaults to "". + + Raises: + PublishDataError: If there is an error in publishing data. + """ if isinstance(payload, str): payload = payload.encode("utf-8") @@ -107,22 +283,14 @@ async def publish_data( req.publish_data.local_participant_handle = self._ffi_handle.handle req.publish_data.data_ptr = ctypes.addressof(cdata) req.publish_data.data_len = data_len - req.publish_data.kind = kind + req.publish_data.reliable = reliable req.publish_data.topic = topic - - sids = [] - for p in destination_sids: - if isinstance(p, RemoteParticipant): - sids.append(p.sid) - else: - sids.append(p) - - req.publish_data.destination_sids.extend(sids) + req.publish_data.destination_identities.extend(destination_identities) queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( + cb: proto_ffi.FfiEvent = await queue.wait_for( lambda e: e.publish_data.async_id == resp.publish_data.async_id ) finally: @@ -131,7 +299,44 @@ async def publish_data( if cb.publish_data.error: raise PublishDataError(cb.publish_data.error) + async def publish_dtmf(self, *, code: int, digit: str) -> None: + """ + Publish SIP DTMF message. + + Args: + code (int): DTMF code. + digit (str): DTMF digit. + + Raises: + PublishDTMFError: If there is an error in publishing SIP DTMF message. + """ + req = proto_ffi.FfiRequest() + req.publish_sip_dtmf.local_participant_handle = self._ffi_handle.handle + req.publish_sip_dtmf.code = code + req.publish_sip_dtmf.digit = digit + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.publish_sip_dtmf.async_id == resp.publish_sip_dtmf.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.publish_sip_dtmf.error: + raise PublishDTMFError(cb.publish_sip_dtmf.error) + async def publish_transcription(self, transcription: Transcription) -> None: + """ + Publish transcription data to the room. + + Args: + transcription (Transcription): The transcription data to publish. + + Raises: + PublishTranscriptionError: If there is an error in publishing transcription. + """ req = proto_ffi.FfiRequest() proto_segments = [ ProtoTranscriptionSegment( @@ -140,6 +345,7 @@ async def publish_transcription(self, transcription: Transcription) -> None: start_time=s.start_time, end_time=s.end_time, final=s.final, + language=s.language, ) for s in transcription.segments ] @@ -147,15 +353,13 @@ async def publish_transcription(self, transcription: Transcription) -> None: req.publish_transcription.local_participant_handle = self._ffi_handle.handle req.publish_transcription.participant_identity = transcription.participant_identity req.publish_transcription.segments.extend(proto_segments) - req.publish_transcription.track_id = transcription.track_id - req.publish_transcription.language = transcription.language + req.publish_transcription.track_id = transcription.track_sid # fmt: on queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( - lambda e: e.publish_transcription.async_id - == resp.publish_transcription.async_id + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.publish_transcription.async_id == resp.publish_transcription.async_id ) finally: FfiClient.instance.queue.unsubscribe(queue) @@ -163,39 +367,447 @@ async def publish_transcription(self, transcription: Transcription) -> None: if cb.publish_transcription.error: raise PublishTranscriptionError(cb.publish_transcription.error) - async def update_metadata(self, metadata: str) -> None: + async def perform_rpc( + self, + *, + destination_identity: str, + method: str, + payload: str, + response_timeout: Optional[float] = None, + max_round_trip_latency: Optional[float] = None, + ) -> str: + """ + Initiate an RPC call to a remote participant. + + Args: + destination_identity (str): The `identity` of the destination participant + method (str): The method name to call + payload (str): The method payload + response_timeout (Optional[float]): Timeout for receiving a response after initial connection + max_round_trip_latency (float): The maximum amount of time it should ever take for an RPC + request to reach the destination and for the ACK to come back. Defaults to 7 seconds to + account for various relay timeouts and retries in LiveKit Cloud that occur in rare cases. + Most callers should not need to change this, but it can be increased to tolerate + high-latency networks where RPC requests are backed up behind other messages on the + data channel. + + Returns: + str: The response payload + + Raises: + RpcError: On failure. Details in `message`. + """ + req = proto_ffi.FfiRequest() + req.perform_rpc.local_participant_handle = self._ffi_handle.handle + req.perform_rpc.destination_identity = destination_identity + req.perform_rpc.method = method + req.perform_rpc.payload = payload + if response_timeout is not None: + req.perform_rpc.response_timeout_ms = int(response_timeout * 1000) + if max_round_trip_latency is not None: + req.perform_rpc.max_round_trip_latency_ms = int(max_round_trip_latency * 1000) + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb = await queue.wait_for(lambda e: e.perform_rpc.async_id == resp.perform_rpc.async_id) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.perform_rpc.HasField("error"): + raise RpcError._from_proto(cb.perform_rpc.error) + + return cast(str, cb.perform_rpc.payload) + + def register_rpc_method( + self, + method_name: str, + handler: Optional[F] = None, + ) -> Union[F, Callable[[F], F]]: + """ + Establishes the participant as a receiver for calls of the specified RPC method. + Can be used either as a decorator or a regular method. + + The handler will receive one argument of type `RpcInvocationData` and should return a string response which will be forwarded back to the caller. + + The handler may be synchronous or asynchronous. + + If unable to respond within `response_timeout`, the caller will hang up and receive an error on their side. + + You may raise errors of type `RpcError` in the handler, and they will be forwarded to the caller. + + Other errors raised in your handler will be caught and forwarded to the caller as "1500 Application Error". + + Args: + method_name (str): The name of the indicated RPC method. + handler (Optional[Callable]): Handler to be invoked whenever an RPC request for this method is received. Omit this argument to use the decorator syntax. + + Returns: + None (when used as a decorator it returns the decorator function) + + Example: + # As a decorator: + @room.local_participant.register_rpc_method("greet") + async def greet_handler(data: RpcInvocationData) -> str: + print(f"Received greeting from {data.caller_identity}: {data.payload}") + return f"Hello, {data.caller_identity}!" + + # As a regular method: + async def greet_handler(data: RpcInvocationData) -> str: + print(f"Received greeting from {data.caller_identity}: {data.payload}") + return f"Hello, {data.caller_identity}!" + + room.local_participant.register_rpc_method('greet', greet_handler) + """ + + def register(handler_func: F) -> F: + self._rpc_handlers[method_name] = handler_func + req = proto_ffi.FfiRequest() + req.register_rpc_method.local_participant_handle = self._ffi_handle.handle + req.register_rpc_method.method = method_name + FfiClient.instance.request(req) + return handler_func + + if handler is not None: + return register(handler) + else: + return register + + def unregister_rpc_method(self, method: str) -> None: + """ + Unregisters a previously registered RPC method. + + Args: + method (str): The name of the RPC method to unregister + """ + self._rpc_handlers.pop(method, None) + + req = proto_ffi.FfiRequest() + req.unregister_rpc_method.local_participant_handle = self._ffi_handle.handle + req.unregister_rpc_method.method = method + + FfiClient.instance.request(req) + + def set_track_subscription_permissions( + self, + *, + allow_all_participants: bool, + participant_permissions: Optional[List[ParticipantTrackPermission]] = None, + ) -> None: + """ + Set the track subscription permissions for the local participant. + + Args: + allow_all_participants (bool): Whether to allow all participants to subscribe to this participant's tracks. + participant_permissions (List[ParticipantTrackPermission]): Participant-specific track subscription permissions, ignored if `allow_all_participants` is True. + """ + if participant_permissions is None: + participant_permissions = [] + req = proto_ffi.FfiRequest() - req.update_local_metadata.local_participant_handle = self._ffi_handle.handle - req.update_local_metadata.metadata = metadata + req.set_track_subscription_permissions.local_participant_handle = self._ffi_handle.handle + req.set_track_subscription_permissions.all_participants_allowed = allow_all_participants + req.set_track_subscription_permissions.permissions.extend(participant_permissions) + FfiClient.instance.request(req) + + async def _handle_rpc_method_invocation( + self, + invocation_id: int, + method: str, + request_id: str, + caller_identity: str, + payload: str, + response_timeout: float, + ) -> None: + response_error: Optional[RpcError] = None + response_payload: Optional[str] = None + + params = RpcInvocationData(request_id, caller_identity, payload, response_timeout) + + handler = self._rpc_handlers.get(method) + + if not handler: + response_error = RpcError._built_in(RpcError.ErrorCode.UNSUPPORTED_METHOD) + else: + try: + if asyncio.iscoroutinefunction(handler): + try: + response_payload = await asyncio.wait_for( + handler(params), timeout=response_timeout + ) + except asyncio.TimeoutError: + raise RpcError._built_in(RpcError.ErrorCode.RESPONSE_TIMEOUT) + except asyncio.CancelledError: + raise RpcError._built_in(RpcError.ErrorCode.RECIPIENT_DISCONNECTED) + else: + response_payload = cast(Optional[str], handler(params)) + except RpcError as error: + response_error = error + except Exception: + logger.exception( + f"Uncaught error returned by RPC handler for {method}. " + "Returning APPLICATION_ERROR instead. " + ) + response_error = RpcError._built_in(RpcError.ErrorCode.APPLICATION_ERROR) + + req = proto_ffi.FfiRequest( + rpc_method_invocation_response=RpcMethodInvocationResponseRequest( + local_participant_handle=self._ffi_handle.handle, + invocation_id=invocation_id, + error=response_error._to_proto() if response_error else None, + payload=response_payload, + ) + ) + + res = FfiClient.instance.request(req) + + if res.rpc_method_invocation_response.error: + err = res.rpc_method_invocation_response.error + logger.error(f"error sending rpc method invocation response: {err}") + + async def set_metadata(self, metadata: str) -> None: + """ + Set the metadata for the local participant. + + Note: this requires `canUpdateOwnMetadata` permission. + + Args: + metadata (str): The new metadata. + """ + req = proto_ffi.FfiRequest() + req.set_local_metadata.local_participant_handle = self._ffi_handle.handle + req.set_local_metadata.metadata = metadata queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) await queue.wait_for( - lambda e: e.update_local_metadata.async_id - == resp.update_local_metadata.async_id + lambda e: e.set_local_metadata.async_id == resp.set_local_metadata.async_id ) finally: FfiClient.instance.queue.unsubscribe(queue) - async def update_name(self, name: str) -> None: + async def set_name(self, name: str) -> None: + """ + Set the name for the local participant. + + Note: this requires `canUpdateOwnMetadata` permission. + + Args: + name (str): The new name. + """ req = proto_ffi.FfiRequest() - req.update_local_name.local_participant_handle = self._ffi_handle.handle - req.update_local_name.name = name + req.set_local_name.local_participant_handle = self._ffi_handle.handle + req.set_local_name.name = name queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) await queue.wait_for( - lambda e: e.update_local_name.async_id - == resp.update_local_name.async_id + lambda e: e.set_local_name.async_id == resp.set_local_name.async_id ) finally: FfiClient.instance.queue.unsubscribe(queue) + async def set_attributes(self, attributes: dict[str, str]) -> None: + """ + Set custom attributes for the local participant. + + Note: this requires `canUpdateOwnMetadata` permission. + + Args: + attributes (dict[str, str]): A dictionary of attributes to set. + """ + req = proto_ffi.FfiRequest() + req.set_local_attributes.local_participant_handle = self._ffi_handle.handle + existing_attributes = { + entry.key: entry.value for entry in req.set_local_attributes.attributes + } + existing_attributes.update(attributes) + + for key, value in existing_attributes.items(): + entry = req.set_local_attributes.attributes.add() + entry.key = key + entry.value = value + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + await queue.wait_for( + lambda e: e.set_local_attributes.async_id == resp.set_local_attributes.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + async def stream_text( + self, + *, + destination_identities: Optional[List[str]] = None, + topic: str = "", + attributes: Optional[Dict[str, str]] = None, + stream_id: str | None = None, + reply_to_id: str | None = None, + total_size: int | None = None, + sender_identity: str | None = None, + ) -> TextStreamWriter: + """ + Returns a TextStreamWriter that allows to write individual chunks of text to a text stream. + In most cases where you want to simply send a text message use send_text() instead. + """ + writer = TextStreamWriter( + self, + topic=topic, + attributes=attributes, + reply_to_id=reply_to_id, + destination_identities=destination_identities, + total_size=total_size, + stream_id=stream_id, + sender_identity=sender_identity, + ) + + await writer._send_header() + + return writer + + async def send_text( + self, + text: str, + *, + destination_identities: Optional[List[str]] = None, + topic: str = "", + attributes: Optional[Dict[str, str]] = None, + reply_to_id: str | None = None, + ) -> TextStreamInfo: + total_size = len(text.encode()) + writer = await self.stream_text( + destination_identities=destination_identities, + topic=topic, + attributes=attributes, + reply_to_id=reply_to_id, + total_size=total_size, + ) + + await writer.write(text) + await writer.aclose() + + return writer.info + + async def stream_bytes( + self, + name: str, + *, + total_size: int | None = None, + mime_type: str = "application/octet-stream", + attributes: Optional[Dict[str, str]] = None, + stream_id: str | None = None, + destination_identities: Optional[List[str]] = None, + topic: str = "", + ) -> ByteStreamWriter: + """ + Returns a ByteStreamWriter that allows to write individual chunks of bytes to a byte stream. + In cases where you want to simply send a file from the file system use send_file() instead. + """ + writer = ByteStreamWriter( + self, + name=name, + attributes=attributes, + total_size=total_size, + stream_id=stream_id, + mime_type=mime_type, + destination_identities=destination_identities, + topic=topic, + ) + + await writer._send_header() + + return writer + + async def send_file( + self, + file_path: str, + *, + topic: str = "", + destination_identities: Optional[List[str]] = None, + attributes: Optional[Dict[str, str]] = None, + stream_id: str | None = None, + ) -> ByteStreamInfo: + file_size = os.path.getsize(file_path) + file_name = os.path.basename(file_path) + mime_type, _ = mimetypes.guess_type(file_path) + if mime_type is None: + mime_type = "application/octet-stream" # Fallback MIME type for unknown files + + writer: ByteStreamWriter = await self.stream_bytes( + name=file_name, + total_size=file_size, + mime_type=mime_type, + attributes=attributes, + stream_id=stream_id, + destination_identities=destination_identities, + topic=topic, + ) + + async with aiofiles.open(file_path, "rb") as f: + while bytes := await f.read(STREAM_CHUNK_SIZE): + await writer.write(bytes) + await writer.aclose() + + return writer.info + + async def publish_data_track( + self, + *, + name: str, + ) -> LocalDataTrack: + """Publishes a data track. + + Args: + name: The track name used to identify the track to other participants. + Must not be empty and must be unique per publisher. + + Returns: + The published data track. Use :meth:`LocalDataTrack.try_push` to + send data frames on the track. + + Raises: + PublishDataTrackError: If there is an error publishing the data track. + """ + proto_opts = proto_data_track.DataTrackOptions(name=name) + + req = proto_ffi.FfiRequest() + req.publish_data_track.local_participant_handle = self._ffi_handle.handle + req.publish_data_track.options.CopyFrom(proto_opts) + + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.publish_data_track.async_id == resp.publish_data_track.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.publish_data_track.HasField("error"): + raise PublishDataTrackError(cb.publish_data_track.error.message) + + return LocalDataTrack(cb.publish_data_track.track) + async def publish_track( self, track: LocalTrack, options: TrackPublishOptions = TrackPublishOptions() ) -> LocalTrackPublication: + """ + Publish a local track to the room. + + Args: + track (LocalTrack): The track to publish. + options (TrackPublishOptions, optional): Options for publishing the track. + + Returns: + LocalTrackPublication: The publication of the published track. + + Raises: + PublishTrackError: If there is an error in publishing the track. + """ req = proto_ffi.FfiRequest() req.publish_track.track_handle = track._ffi_handle.handle req.publish_track.local_participant_handle = self._ffi_handle.handle @@ -204,7 +816,7 @@ async def publish_track( queue = self._room_queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( + cb: proto_ffi.FfiEvent = await queue.wait_for( lambda e: e.publish_track.async_id == resp.publish_track.async_id ) @@ -212,9 +824,9 @@ async def publish_track( raise PublishTrackError(cb.publish_track.error) track_publication = LocalTrackPublication(cb.publish_track.publication) - track_publication.track = track + track_publication._track = track track._info.sid = track_publication.sid - self.tracks[track_publication.sid] = track_publication + self._track_publications[track_publication.sid] = track_publication queue.task_done() return track_publication @@ -222,28 +834,70 @@ async def publish_track( self._room_queue.unsubscribe(queue) async def unpublish_track(self, track_sid: str) -> None: + """ + Unpublish a track from the room. + + Args: + track_sid (str): The SID of the track to unpublish. + + Raises: + UnpublishTrackError: If there is an error in unpublishing the track. + """ + # Capture the publication before the FFI round-trip. The + # local_track_unpublished room event races this async response and may + # remove it from _track_publications first; holding our own reference + # guarantees the track is cleared once unpublish completes, regardless + # of which path removes the publication from the dict. + publication = self._track_publications.get(track_sid) + req = proto_ffi.FfiRequest() req.unpublish_track.local_participant_handle = self._ffi_handle.handle req.unpublish_track.track_sid = track_sid + req.unpublish_track.stop_on_unpublish = True queue = self._room_queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( + cb: proto_ffi.FfiEvent = await queue.wait_for( lambda e: e.unpublish_track.async_id == resp.unpublish_track.async_id ) if cb.unpublish_track.error: raise UnpublishTrackError(cb.unpublish_track.error) - publication = self.tracks.pop(track_sid) - publication.track = None + # Remove defensively: the room-event handler may already have done + # so when it processed local_track_unpublished first. + self._track_publications.pop(track_sid, None) + if publication is not None: + # Clear the processor's room context here too: this path races + # the local_track_unpublished room event, and whichever loses + # the race finds the publication already gone and skips its own + # _set_room(None). Calling it from both paths guarantees the + # processor is cleared (and the token_refreshed listener + # detached) exactly once it matters; _set_room(None) is + # idempotent, so a double-clear when this path wins is safe. + if publication._track is not None: + publication._track._set_room(None) + publication._track = None queue.task_done() finally: self._room_queue.unsubscribe(queue) + def __repr__(self) -> str: + return f"rtc.LocalParticipant(sid={self.sid}, identity={self.identity}, name={self.name})" + class RemoteParticipant(Participant): def __init__(self, owned_info: proto_participant.OwnedParticipant) -> None: super().__init__(owned_info) - self.tracks: dict[str, RemoteTrackPublication] = {} # type: ignore + self._track_publications: dict[str, RemoteTrackPublication] = {} + + @property + def track_publications(self) -> Mapping[str, RemoteTrackPublication]: + """ + A dictionary of track publications associated with the participant. + """ + return self._track_publications + + def __repr__(self) -> str: + return f"rtc.RemoteParticipant(sid={self.sid}, identity={self.identity}, name={self.name})" diff --git a/livekit-rtc/livekit/rtc/platform_audio.py b/livekit-rtc/livekit/rtc/platform_audio.py new file mode 100644 index 00000000..ce168de8 --- /dev/null +++ b/livekit-rtc/livekit/rtc/platform_audio.py @@ -0,0 +1,426 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +PlatformAudio - Platform audio device management via WebRTC's ADM + +PlatformAudio provides access to the platform's audio devices (microphones and +speakers) via WebRTC's Audio Device Module (ADM). This is the recommended way +to handle audio in most applications because: + +- Built-in echo cancellation (AEC), noise suppression (NS), and auto gain control (AGC) +- Automatic device enumeration and selection +- Efficient hardware-accelerated audio processing on supported platforms +- No need for external audio libraries + +For custom audio processing pipelines where you need direct access to audio frames, +use the synthetic mode with AudioSource instead. See AudioSource for more details. + +# Usage + +```python +from livekit import rtc + +# Create PlatformAudio instance (enables ADM) +platform_audio = rtc.PlatformAudio() + +# List available devices +print("Microphones:") +for device in platform_audio.recording_devices(): + print(f" [{device.index}] {device.name} (ID: {device.id})") + +print("Speakers:") +for device in platform_audio.playout_devices(): + print(f" [{device.index}] {device.name} (ID: {device.id})") + +# Select specific devices (optional - uses default if not called) +platform_audio.set_recording_device(mic_device.id) +platform_audio.set_playout_device(speaker_device.id) + +# Create audio source for publishing +audio_source = platform_audio.create_audio_source() + +# Create and publish track +track = rtc.LocalAudioTrack.create_audio_track("microphone", audio_source) +await room.local_participant.publish_track(track) +``` + +# Limitations + +- No direct access to captured audio frames (ADM sends directly to WebRTC) +- For frame-level processing, use synthetic mode with AudioSource.capture_frame() +""" + +from __future__ import annotations + +from dataclasses import dataclass +from typing import List, Optional + +from ._ffi_client import FfiHandle, FfiClient +from ._proto import audio_frame_pb2 as proto_audio_frame +from ._proto import ffi_pb2 as proto_ffi + + +class PlatformAudioError(Exception): + """Exception raised when PlatformAudio operations fail.""" + + pass + + +@dataclass +class AudioDeviceInfo: + """Information about an audio device. + + Attributes: + index: Device index (0-based). Note: indices can change when devices are + added/removed, so prefer using `id` for device selection. + name: Device name as reported by the operating system. + id: Platform-specific unique device identifier (GUID). This is stable across + device additions/removals and should be preferred over index for device + selection. + """ + + index: int + name: str + id: str + + @classmethod + def _from_proto(cls, proto: proto_audio_frame.AudioDeviceInfo) -> "AudioDeviceInfo": + return cls(index=proto.index, name=proto.name, id=proto.guid) + + +@dataclass +class PlatformAudioOptions: + """Audio processing options for PlatformAudio. + + These options configure WebRTC's audio processing pipeline when using + PlatformAudio for microphone capture. + + Attributes: + echo_cancellation: Enable acoustic echo cancellation (AEC) to remove + echo from speaker playback. Default: True. + noise_suppression: Enable noise suppression to remove background noise. + Default: True. + auto_gain_control: Enable automatic gain control to normalize audio levels. + Default: True. + prefer_hardware: Prefer hardware audio processing when available. + - iOS: Uses Voice Processing I/O (VPIO) for low-latency processing + - Android: Hardware support varies by device + - Desktop: Generally not available + Default: True on iOS, False on other platforms. + """ + + echo_cancellation: bool = True + noise_suppression: bool = True + auto_gain_control: bool = True + prefer_hardware: bool = False + + def _to_proto(self) -> proto_audio_frame.AudioSourceOptions: + return proto_audio_frame.AudioSourceOptions( + echo_cancellation=self.echo_cancellation, + noise_suppression=self.noise_suppression, + auto_gain_control=self.auto_gain_control, + prefer_hardware=self.prefer_hardware, + ) + + +class PlatformAudioSource: + """Audio source backed by PlatformAudio (WebRTC ADM). + + This source captures audio from the selected microphone via the platform's + Audio Device Module. Unlike AudioSource (synthetic mode), frames are captured + and sent directly by the ADM - there is no `capture_frame()` method. + + Use this with LocalAudioTrack.create_audio_track() to publish microphone audio. + + Note: This class is created via PlatformAudio.create_audio_source() and should + not be instantiated directly. + + Resource Management: + Call `close()` when done to immediately release native resources. If not + called, resources are released when the object is garbage collected, but + GC timing is non-deterministic and may cause issues on some platforms + (especially Windows) where audio device handles must be released promptly. + """ + + def __init__(self, ffi_handle: FfiHandle, info: proto_audio_frame.AudioSourceInfo): + self._ffi_handle = ffi_handle + self._info = info + + @property + def _handle(self) -> int: + """Internal FFI handle for use with LocalAudioTrack.create_audio_track().""" + return self._ffi_handle.handle + + def close(self) -> None: + """Release the native audio source resources. + + Call this method when you are done using the audio source to immediately + release the underlying native handle. This is especially important on + Windows where audio device handles must be released promptly to avoid + interfering with other audio operations. + + If `close()` is not called, resources will be released when the object + is garbage collected. However, Python's garbage collection timing is + non-deterministic, which can lead to: + - Audio device contention if creating new audio sources before old ones + are collected + - Test failures when running multiple audio tests in sequence + - Resource leaks in long-running applications that frequently create + and discard audio sources + + It is safe to call `close()` multiple times; subsequent calls are no-ops. + """ + self._ffi_handle.dispose() + + +class PlatformAudio: + """Platform audio device management via WebRTC's Audio Device Module (ADM). + + PlatformAudio provides the recommended way to handle audio capture and playback + in LiveKit applications. It uses WebRTC's ADM for efficient, hardware-accelerated + audio processing with built-in echo cancellation, noise suppression, and + automatic gain control. + + Key features: + - Device enumeration: List available microphones and speakers + - Device selection: Choose specific input/output devices + - Audio processing: Built-in AEC, NS, and AGC + - Automatic playout: Received audio is automatically played through speakers + + Example: + ```python + # Create PlatformAudio (keeps ADM alive) + platform_audio = rtc.PlatformAudio() + + # Enumerate and select devices + mics = platform_audio.recording_devices() + platform_audio.set_recording_device(mics[0].id) + + # Create source and publish track + source = platform_audio.create_audio_source() + track = rtc.LocalAudioTrack.create_audio_track("mic", source) + await room.local_participant.publish_track(track) + + # When done, close resources + source.close() + platform_audio.close() + ``` + + Resource Management: + Call `close()` when done to immediately release native ADM resources. + If not called, resources are released via garbage collection, but GC + timing is non-deterministic. On Windows especially, failing to promptly + release ADM resources can cause audio device contention and interfere + with subsequent audio operations (including synthetic AudioSource usage). + """ + + def __init__(self) -> None: + """Initialize PlatformAudio and enable the Audio Device Module. + + Raises: + PlatformAudioError: If ADM initialization fails. + """ + req = proto_ffi.FfiRequest() + req.new_platform_audio.SetInParent() + + resp = FfiClient.instance.request(req) + + if resp.new_platform_audio.HasField("error"): + raise PlatformAudioError( + f"Failed to initialize PlatformAudio: {resp.new_platform_audio.error}" + ) + + self._info = resp.new_platform_audio.platform_audio.info + self._ffi_handle = FfiHandle(resp.new_platform_audio.platform_audio.handle.id) + + def recording_devices(self) -> List[AudioDeviceInfo]: + """Get available recording devices (microphones). + + Returns: + List of AudioDeviceInfo for each available microphone. + + Raises: + PlatformAudioError: If device enumeration fails. + """ + req = proto_ffi.FfiRequest() + req.get_audio_devices.platform_audio_handle = self._ffi_handle.handle + + resp = FfiClient.instance.request(req) + + if resp.get_audio_devices.HasField("error") and resp.get_audio_devices.error: + raise PlatformAudioError( + f"Failed to enumerate recording devices: {resp.get_audio_devices.error}" + ) + + return [AudioDeviceInfo._from_proto(d) for d in resp.get_audio_devices.recording_devices] + + def playout_devices(self) -> List[AudioDeviceInfo]: + """Get available playout devices (speakers/headphones). + + Returns: + List of AudioDeviceInfo for each available speaker. + + Raises: + PlatformAudioError: If device enumeration fails. + """ + req = proto_ffi.FfiRequest() + req.get_audio_devices.platform_audio_handle = self._ffi_handle.handle + + resp = FfiClient.instance.request(req) + + if resp.get_audio_devices.HasField("error") and resp.get_audio_devices.error: + raise PlatformAudioError( + f"Failed to enumerate playout devices: {resp.get_audio_devices.error}" + ) + + return [AudioDeviceInfo._from_proto(d) for d in resp.get_audio_devices.playout_devices] + + def set_recording_device(self, device_id: str) -> None: + """Select the recording device (microphone) to use. + + Call this before creating audio tracks to select which microphone to use. + If not called, the system default microphone is used. + + Args: + device_id: The device ID (GUID) from AudioDeviceInfo.id. Use the ID + rather than index for stable device selection across hot-plug events. + + Raises: + PlatformAudioError: If device selection fails. + """ + req = proto_ffi.FfiRequest() + req.set_recording_device.platform_audio_handle = self._ffi_handle.handle + req.set_recording_device.device_id = device_id + + resp = FfiClient.instance.request(req) + + if resp.set_recording_device.HasField("error") and resp.set_recording_device.error: + raise PlatformAudioError( + f"Failed to set recording device: {resp.set_recording_device.error}" + ) + + def set_playout_device(self, device_id: str) -> None: + """Select the playout device (speaker) to use. + + Call this before connecting to a room to select which speaker to use + for audio output. If not called, the system default speaker is used. + + Args: + device_id: The device ID (GUID) from AudioDeviceInfo.id. Use the ID + rather than index for stable device selection across hot-plug events. + + Raises: + PlatformAudioError: If device selection fails. + """ + req = proto_ffi.FfiRequest() + req.set_playout_device.platform_audio_handle = self._ffi_handle.handle + req.set_playout_device.device_id = device_id + + resp = FfiClient.instance.request(req) + + if resp.set_playout_device.HasField("error") and resp.set_playout_device.error: + raise PlatformAudioError( + f"Failed to set playout device: {resp.set_playout_device.error}" + ) + + def create_audio_source( + self, options: Optional[PlatformAudioOptions] = None + ) -> PlatformAudioSource: + """Create an audio source for publishing microphone audio. + + The returned source captures audio from the selected microphone (or system + default) and can be used with LocalAudioTrack.create_audio_track() to + publish audio. + + Args: + options: Audio processing options (AEC, NS, AGC, prefer_hardware). + If None, defaults are used (all processing enabled). + + Returns: + PlatformAudioSource that can be passed to LocalAudioTrack.create_audio_track(). + + Raises: + PlatformAudioError: If source creation fails. + + Example: + ```python + source = platform_audio.create_audio_source( + PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + ) + ) + track = rtc.LocalAudioTrack.create_audio_track("microphone", source) + ``` + """ + if options is None: + options = PlatformAudioOptions() + + req = proto_ffi.FfiRequest() + req.new_audio_source.type = proto_audio_frame.AudioSourceType.AUDIO_SOURCE_PLATFORM + req.new_audio_source.platform_audio_handle = self._ffi_handle.handle + req.new_audio_source.options.CopyFrom(options._to_proto()) + # For platform audio, the ADM determines the actual sample rate and channels. + # These fields are ignored but required by older proto versions (will be optional + # in livekit-ffi >= 0.12.58). Use standard WebRTC defaults. + req.new_audio_source.sample_rate = 48000 + req.new_audio_source.num_channels = 1 + + resp = FfiClient.instance.request(req) + source_info = resp.new_audio_source.source + + return PlatformAudioSource( + FfiHandle(source_info.handle.id), + source_info, + ) + + def close(self) -> None: + """Release the native Audio Device Module resources. + + Call this method when you are done using PlatformAudio to immediately + release the underlying ADM handle and associated native resources. + + Why call close() explicitly? + Python's garbage collection is non-deterministic. If you rely on GC + to clean up PlatformAudio, the ADM may remain active longer than + expected. This can cause problems on some platforms (especially + Windows) where: + + - Audio device handles are held longer than necessary, preventing + other applications or audio subsystems from accessing the devices + - Subsequent audio operations (including synthetic AudioSource) may + fail or behave unexpectedly due to ADM still being active + - Tests running in sequence may interfere with each other if the + previous test's ADM resources haven't been collected yet + + What happens if close() is not called? + Resources will eventually be released when the PlatformAudio object + is garbage collected. This works fine in simple applications where + the object goes out of scope and is collected promptly. However, in + these scenarios you should call close() explicitly: + + - Running multiple tests that use audio + - Switching between PlatformAudio and synthetic AudioSource + - Long-running applications that create/destroy audio resources + - Applications where prompt device release is important + + It is safe to call `close()` multiple times; subsequent calls are no-ops. + + Note: + Always close PlatformAudioSource instances before closing the parent + PlatformAudio instance. + """ + self._ffi_handle.dispose() diff --git a/livekit-rtc/livekit/rtc/resources/.gitattributes b/livekit-rtc/livekit/rtc/resources/.gitattributes new file mode 100644 index 00000000..c27efeb3 --- /dev/null +++ b/livekit-rtc/livekit/rtc/resources/.gitattributes @@ -0,0 +1 @@ +jupyter-html/* linguist-generated=true \ No newline at end of file diff --git a/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html b/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html new file mode 100644 index 00000000..6e35aa15 --- /dev/null +++ b/livekit-rtc/livekit/rtc/resources/jupyter-html/index.html @@ -0,0 +1,101 @@ + + + + + + + Vite + React + TS + + + + +
+ + diff --git a/livekit-rtc/livekit/rtc/room.py b/livekit-rtc/livekit/rtc/room.py index 2221eb41..83cb06ee 100644 --- a/livekit-rtc/livekit/rtc/room.py +++ b/livekit-rtc/livekit/rtc/room.py @@ -11,31 +11,55 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations +import datetime import asyncio import ctypes import logging from dataclasses import dataclass, field -from typing import Dict, Literal, Optional +from typing import Callable, Dict, Literal, Optional, cast, Mapping +import warnings -from ._event_emitter import EventEmitter +from .event_emitter import EventEmitter from ._ffi_client import FfiClient, FfiHandle from ._proto import ffi_pb2 as proto_ffi from ._proto import participant_pb2 as proto_participant from ._proto import room_pb2 as proto_room -from ._proto.room_pb2 import ConnectionState +from ._proto import stats_pb2 as proto_stats +from ._proto.participant_pb2 import DisconnectReason +from ._proto.room_pb2 import ConnectionState, SimulateScenarioKind from ._proto.track_pb2 import TrackKind +from ._proto.rpc_pb2 import RpcMethodInvocationEvent from ._utils import BroadcastQueue from .e2ee import E2EEManager, E2EEOptions -from .participant import LocalParticipant, Participant, RemoteParticipant +from .log import logger +from .participant import ( + LocalParticipant, + Participant, + RemoteParticipant, + _connection_quality_from_proto, +) from .track import RemoteAudioTrack, RemoteVideoTrack -from .track_publication import RemoteTrackPublication +from .track_publication import RemoteTrackPublication, TrackPublication +from .transcription import TranscriptionSegment +from .data_stream import ( + TextStreamReader, + ByteStreamReader, + TextStreamHandler, + ByteStreamHandler, +) +from .data_track import RemoteDataTrack + EventTypes = Literal[ "participant_connected", "participant_disconnected", + "participant_active", "local_track_published", "local_track_unpublished", + "local_track_republished", + "local_track_subscribed", "track_published", "track_unpublished", "track_subscribed", @@ -47,15 +71,24 @@ "room_metadata_changed", "participant_metadata_changed", "participant_name_changed", + "participant_attributes_changed", "connection_quality_changed", + "participant_encryption_status_changed", + "participant_permissions_changed", "data_received", "sip_dtmf_received", + "transcription_received", "e2ee_state_changed", "connection_state_changed", "connected", "disconnected", "reconnecting", "reconnected", + "room_updated", + "moved", + "token_refreshed", + "data_track_published", + "data_track_unpublished", ] @@ -64,37 +97,59 @@ class RtcConfiguration: ice_transport_type: proto_room.IceTransportType.ValueType = ( proto_room.IceTransportType.TRANSPORT_ALL ) + """Specifies the type of ICE transport to be used (e.g., all, relay, etc.).""" continual_gathering_policy: proto_room.ContinualGatheringPolicy.ValueType = ( proto_room.ContinualGatheringPolicy.GATHER_CONTINUALLY ) + """Policy for continual gathering of ICE candidates.""" ice_servers: list[proto_room.IceServer] = field(default_factory=list) + """List of ICE servers for STUN/TURN. When empty, it uses the default ICE servers provided by + the SFU.""" @dataclass class RoomOptions: auto_subscribe: bool = True + """Automatically subscribe to tracks when participants join.""" dynacast: bool = False - e2ee: Optional[E2EEOptions] = None - rtc_config: Optional[RtcConfiguration] = None + e2ee: E2EEOptions | None = None + """Deprecated, use `encryption` field instead""" + encryption: E2EEOptions | None = None + """Options for end-to-end encryption.""" + rtc_config: RtcConfiguration | None = None + """WebRTC-related configuration.""" + connect_timeout: float | None = None + """Timeout in seconds for each signal connection attempt. When None, uses the default (5s).""" + single_peer_connection: bool | None = None + """Use a single peer connection for both publish and subscribe. When None, uses the default (false).""" @dataclass class DataPacket: data: bytes + """The payload of the data packet.""" kind: proto_room.DataPacketKind.ValueType - participant: Optional[RemoteParticipant] = ( - None # None when the data has been sent by a server SDK - ) - topic: Optional[str] = None + """Type of the data packet (e.g., RELIABLE, LOSSY).""" + participant: RemoteParticipant | None + """Participant who sent the data. None when sent by a server SDK.""" + topic: str | None = None + """Topic associated with the data packet.""" @dataclass class SipDTMF: code: int + """DTMF code corresponding to the digit.""" digit: str - participant: Optional[RemoteParticipant] = ( - None # None when the data has been sent by a server SDK - ) + """DTMF digit sent.""" + participant: RemoteParticipant | None = None + """Participant who sent the DTMF digit. None when sent by a server SDK.""" + + +@dataclass +class RtcStats: + publisher_stats: list[proto_stats.RtcStats] + subscriber_stats: list[proto_stats.RtcStats] class ConnectError(Exception): @@ -103,47 +158,307 @@ def __init__(self, message: str): class Room(EventEmitter[EventTypes]): - def __init__(self, loop: Optional[asyncio.AbstractEventLoop] = None) -> None: + def __init__( + self, + loop: Optional[asyncio.AbstractEventLoop] = None, + ) -> None: + """Initializes a new Room instance. + + Parameters: + loop (Optional[asyncio.AbstractEventLoop]): The event loop to use. If not provided, the default event loop is used. + """ super().__init__() self._ffi_handle: Optional[FfiHandle] = None self._loop = loop or asyncio.get_event_loop() self._room_queue = BroadcastQueue[proto_ffi.FfiEvent]() self._info = proto_room.RoomInfo() + self._rpc_invocation_tasks: set[asyncio.Task] = set() + self._data_stream_tasks: set[asyncio.Task] = set() + + self._remote_participants: Dict[str, RemoteParticipant] = {} + self._connection_state = ConnectionState.CONN_DISCONNECTED + self._first_sid_future = asyncio.Future[str]() + self._local_participant: LocalParticipant | None = None - self.participants: Dict[str, RemoteParticipant] = {} - self.participants_by_identity: Dict[str, RemoteParticipant] = {} - self.connection_state = ConnectionState.CONN_DISCONNECTED + self._text_stream_readers: Dict[str, TextStreamReader] = {} + self._byte_stream_readers: Dict[str, ByteStreamReader] = {} + self._text_stream_handlers: Dict[str, TextStreamHandler] = {} + self._byte_stream_handlers: Dict[str, ByteStreamHandler] = {} + + self._token: str | None = None + self._server_url: str | None = None def __del__(self) -> None: if self._ffi_handle is not None: FfiClient.instance.queue.unsubscribe(self._ffi_queue) @property - def sid(self) -> str: - return self._info.sid + async def sid(self) -> str: + """Asynchronously retrieves the session ID (SID) of the room. + + Returns: + str: The session ID of the room. + """ + if self._info.sid: + return self._info.sid + + # This future is shared state for the Room. Caller-side timeouts, e.g. + # asyncio.wait_for(room.sid, ...), should not cancel it for future waiters. + return await asyncio.shield(self._first_sid_future) + + def _resolve_first_sid(self, sid: str) -> None: + # The room SID can arrive through the initial connect result or later + # room update events. Resolve the first-SID waiter from any source that + # carries it so waiters do not depend on event ordering. + if sid and not self._first_sid_future.done(): + self._first_sid_future.set_result(sid) + + @property + def local_participant(self) -> LocalParticipant: + """Gets the local participant in the room. + + Returns: + LocalParticipant: The local participant in the room. + """ + if self._local_participant is None: + raise Exception("cannot access local participant before connecting") + + return self._local_participant + + @property + def connection_state(self) -> ConnectionState.ValueType: + """Gets the connection state of the room. + + Returns: + ConnectionState: The connection state of the room. + """ + return self._connection_state + + @property + def remote_participants(self) -> Mapping[str, RemoteParticipant]: + """Gets the remote participants in the room. + + Returns: + dict[str, RemoteParticipant]: A dictionary of remote participants indexed by their + identity. + """ + return self._remote_participants @property def name(self) -> str: + """Gets the name of the room. + + Returns: + str: The name of the room. + """ return self._info.name @property def metadata(self) -> str: + """Gets the metadata associated with the room. + + Returns: + str: The metadata of the room. + """ return self._info.metadata @property def e2ee_manager(self) -> E2EEManager: + """Gets the end-to-end encryption (E2EE) manager for the room. + + Returns: + E2EEManager: The E2EE manager instance. + """ return self._e2ee_manager + @property + def num_participants(self) -> int: + """Gets the number of participants in the room. + This value is updated periodically, and is eventually consistent. + + Returns: + int: The number of participants in the room. + """ + return self._info.num_participants + + @property + def num_publishers(self) -> int: + """Gets the number of publishers in the room. + This value is updated periodically, and is eventually consistent. + + Returns: + int: The number of publishers in the room. + """ + return self._info.num_publishers + + @property + def creation_time(self) -> datetime.datetime: + """Time when the room was created. + + Returns: + datetime.datetime: The creation time of the room. + """ + return datetime.datetime.fromtimestamp( + self._info.creation_time / 1000, datetime.timezone.utc + ) + + @property + def is_recording(self) -> bool: + """Whether the room is actively recording. + + Returns: + bool: True if actively recording, False otherwise. + """ + return self._info.active_recording + + @property + def departure_timeout(self) -> float: + """Amount of time to hold the room open after the last standard participant leaves. + + Returns: + float: The departure timeout of the room. + """ + return float(self._info.departure_timeout) + + @property + def empty_timeout(self) -> float: + """Amount of time to keep the room open if no participants join. + + Returns: + float: The empty timeout of the room. + """ + return float(self._info.empty_timeout) + def isconnected(self) -> bool: + """Checks if the room is currently connected. + + Returns: + bool: True if connected, False otherwise. + """ return ( self._ffi_handle is not None - and self.connection_state != ConnectionState.CONN_DISCONNECTED + and self._connection_state != ConnectionState.CONN_DISCONNECTED ) - async def connect( - self, url: str, token: str, options: RoomOptions = RoomOptions() - ) -> None: + def on(self, event: EventTypes, callback: Optional[Callable] = None) -> Callable: + """Registers an event handler for a specific event type. + + Parameters: + event (EventTypes): The name of the event to listen for. + callback (Callable): The function to call when the event occurs. + + Returns: + Callable: The registered callback function. + + Available events: + - **"participant_connected"**: Called when a new participant joins the room. + - Arguments: `participant` (RemoteParticipant) + - **"participant_disconnected"**: Called when a participant leaves the room. + - Arguments: `participant` (RemoteParticipant) + - **"participant_active"**: Called when a remote participant becomes active and is ready to receive data messages. + - Arguments: `participant` (RemoteParticipant) + - **"local_track_published"**: Called when a local track is published. + - Arguments: `publication` (LocalTrackPublication), `track` (Track) + - **"local_track_unpublished"**: Called when a local track is unpublished. + - Arguments: `publication` (LocalTrackPublication) + - **"local_track_republished"**: Called when the SDK auto-republished a local + track during a full reconnect. The publication object is updated in place + with the new server-assigned SIDs (its previous SID is passed alongside so + callers can reconcile any external state keyed by the old SID). + - Arguments: `publication` (LocalTrackPublication), `previous_sid` (str) + - **"local_track_subscribed"**: Called when a local track is subscribed. + - Arguments: `track` (Track) + - **"track_published"**: Called when a remote participant publishes a track. + - Arguments: `publication` (RemoteTrackPublication), `participant` (RemoteParticipant) + - **"track_unpublished"**: Called when a remote participant unpublishes a track. + - Arguments: `publication` (RemoteTrackPublication), `participant` (RemoteParticipant) + - **"track_subscribed"**: Called when a track is subscribed. + - Arguments: `track` (Track), `publication` (RemoteTrackPublication), `participant` (RemoteParticipant) + - **"track_unsubscribed"**: Called when a track is unsubscribed. + - Arguments: `track` (Track), `publication` (RemoteTrackPublication), `participant` (RemoteParticipant) + - **"track_subscription_failed"**: Called when a track subscription fails. + - Arguments: `participant` (RemoteParticipant), `track_sid` (str), `error` (str) + - **"track_muted"**: Called when a track is muted. + - Arguments: `participant` (Participant), `publication` (TrackPublication) + - **"track_unmuted"**: Called when a track is unmuted. + - Arguments: `participant` (Participant), `publication` (TrackPublication) + - **"active_speakers_changed"**: Called when the list of active speakers changes. + - Arguments: `speakers` (list[Participant]) + - **"room_metadata_changed"**: Called when the room's metadata is updated. + - Arguments: `old_metadata` (str), `new_metadata` (str) + - **"participant_metadata_changed"**: Called when a participant's metadata is updated. + - Arguments: `participant` (Participant), `old_metadata` (str), `new_metadata` (str) + - **"participant_name_changed"**: Called when a participant's name is changed. + - Arguments: `participant` (Participant), `old_name` (str), `new_name` (str) + - **"participant_attributes_changed"**: Called when a participant's attributes change. + - Arguments: `changed_attributes` (dict), `participant` (Participant) + - **"participant_encryption_status_changed"**: Called when a participant's encryption status changes. + - Arguments `is_encrypted` (bool), `participant` (Participant) + - **"connection_quality_changed"**: Called when a participant's connection quality changes. + - Arguments: `participant` (Participant), `quality` (ConnectionQuality) + - **"transcription_received"**: Called when a transcription is received. + - Arguments: `segments` (list[TranscriptionSegment]), `participant` (Participant), `publication` (TrackPublication) + - **"data_received"**: Called when data is received. + - Arguments: `data_packet` (DataPacket) + - **"sip_dtmf_received"**: Called when a SIP DTMF signal is received. + - Arguments: `sip_dtmf` (SipDTMF) + - **"e2ee_state_changed"**: Called when a participant's E2EE state changes. + - Arguments: `participant` (Participant), `state` (EncryptionState) + - **"connection_state_changed"**: Called when the room's connection state changes. + - Arguments: `connection_state` (ConnectionState) + - **"connected"**: Called when the room is successfully connected. + - Arguments: None + - **"disconnected"**: Called when the room is disconnected. + - Arguments: `reason` (DisconnectReason) + - **"reconnecting"**: Called when the room is attempting to reconnect. + - Arguments: None + - **"reconnected"**: Called when the room has successfully reconnected. + - Arguments: None + - **"room_updated"**: Called when any information about the room is updated. + - Arguments: None + - **"moved"**: Called when the participant has been moved to another room. + - Arguments: None + - **"data_track_published"**: Called when a remote participant publishes a data track. + - Arguments: `track` (RemoteDataTrack) + - **"data_track_unpublished"**: Called when a remote participant unpublishes a data track. + - Arguments: `sid` (str) + + Example: + ```python + def on_participant_connected(participant): + print(f"Participant connected: {participant.identity}") + + room.on("participant_connected", on_participant_connected) + ``` + """ + return super().on(event, callback) + + async def connect(self, url: str, token: str, options: RoomOptions = RoomOptions()) -> None: + """Connects to a LiveKit room using the specified URL and token. + + Parameters: + url (str): The WebSocket URL of the LiveKit server to connect to. + token (str): The access token for authentication and authorization. + options (RoomOptions, optional): Additional options for the room connection. + + Raises: + ConnectError: If the connection fails. + + Example: + ```python + room = Room() + + # Listen for events before connecting to the room + @room.on("participant_connected") + def on_participant_connected(participant): + print(f"Participant connected: {participant.identity}") + + await room.connect("ws://localhost:7880", "your_token") + ``` + """ + self._server_url = url + self._token = token req = proto_ffi.FfiRequest() req.connect.url = url req.connect.token = token @@ -152,11 +467,29 @@ async def connect( req.connect.options.auto_subscribe = options.auto_subscribe req.connect.options.dynacast = options.dynacast + # The Python SDK still implements data streams in Python on top of raw FFI + # packets, so always advertise only legacy (v1) data stream support to other + # clients. + req.connect.options.data_stream.use_legacy_client_implementation = True + + if options.connect_timeout is not None: + req.connect.options.connect_timeout_ms = int(options.connect_timeout * 1000) + + if options.single_peer_connection is not None: + req.connect.options.single_peer_connection = options.single_peer_connection + if options.e2ee: - req.connect.options.e2ee.encryption_type = options.e2ee.encryption_type - req.connect.options.e2ee.key_provider_options.shared_key = ( - options.e2ee.key_provider_options.shared_key # type: ignore + warnings.warn( + "options.e2ee is deprecated, use options.encryption instead", + DeprecationWarning, + stacklevel=2, ) + + req.connect.options.e2ee.encryption_type = options.e2ee.encryption_type + if options.e2ee.key_provider_options.shared_key is not None: + req.connect.options.e2ee.key_provider_options.shared_key = ( + options.e2ee.key_provider_options.shared_key + ) req.connect.options.e2ee.key_provider_options.ratchet_salt = ( options.e2ee.key_provider_options.ratchet_salt ) @@ -166,17 +499,43 @@ async def connect( req.connect.options.e2ee.key_provider_options.ratchet_window_size = ( options.e2ee.key_provider_options.ratchet_window_size ) + req.connect.options.e2ee.key_provider_options.key_ring_size = ( + options.e2ee.key_provider_options.key_ring_size + ) + req.connect.options.e2ee.key_provider_options.key_derivation_function = ( + options.e2ee.key_provider_options.key_derivation_function + ) + + if options.encryption: + req.connect.options.encryption.encryption_type = options.encryption.encryption_type + if options.encryption.key_provider_options.shared_key is not None: + req.connect.options.encryption.key_provider_options.shared_key = ( + options.encryption.key_provider_options.shared_key + ) + req.connect.options.encryption.key_provider_options.ratchet_salt = ( + options.encryption.key_provider_options.ratchet_salt + ) + req.connect.options.encryption.key_provider_options.failure_tolerance = ( + options.encryption.key_provider_options.failure_tolerance + ) + req.connect.options.encryption.key_provider_options.ratchet_window_size = ( + options.encryption.key_provider_options.ratchet_window_size + ) + req.connect.options.encryption.key_provider_options.key_ring_size = ( + options.encryption.key_provider_options.key_ring_size + ) + req.connect.options.encryption.key_provider_options.key_derivation_function = ( + options.encryption.key_provider_options.key_derivation_function + ) if options.rtc_config: req.connect.options.rtc_config.ice_transport_type = ( options.rtc_config.ice_transport_type - ) # type: ignore + ) req.connect.options.rtc_config.continual_gathering_policy = ( options.rtc_config.continual_gathering_policy - ) # type: ignore - req.connect.options.rtc_config.ice_servers.extend( - options.rtc_config.ice_servers ) + req.connect.options.rtc_config.ice_servers.extend(options.rtc_config.ice_servers) # subscribe before connecting so we don't miss any events self._ffi_queue = FfiClient.instance.queue.subscribe(self._loop) @@ -184,7 +543,7 @@ async def connect( queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( + cb: proto_ffi.FfiEvent = await queue.wait_for( lambda e: e.connect.async_id == resp.connect.async_id ) finally: @@ -194,52 +553,155 @@ async def connect( FfiClient.instance.queue.unsubscribe(self._ffi_queue) raise ConnectError(cb.connect.error) - self._ffi_handle = FfiHandle(cb.connect.room.handle.id) + self._ffi_handle = FfiHandle(cb.connect.result.room.handle.id) - self._e2ee_manager = E2EEManager(self._ffi_handle.handle, options.e2ee) + self._e2ee_manager = E2EEManager( + self._ffi_handle.handle, options.encryption or options.e2ee + ) - self._info = cb.connect.room.info - self.connection_state = ConnectionState.CONN_CONNECTED + self._info = cb.connect.result.room.info + self._resolve_first_sid(self._info.sid) + self._connection_state = ConnectionState.CONN_CONNECTED - self.local_participant = LocalParticipant( - self._room_queue, cb.connect.local_participant + self._local_participant = LocalParticipant( + self._room_queue, cb.connect.result.local_participant ) - for pt in cb.connect.participants: + for pt in cb.connect.result.participants: rp = self._create_remote_participant(pt.participant) # add the initial remote participant tracks for owned_publication_info in pt.publications: publication = RemoteTrackPublication(owned_publication_info) - rp.tracks[publication.sid] = publication + rp._track_publications[publication.sid] = publication # start listening to room events self._task = self._loop.create_task(self._listen_task()) - async def disconnect(self) -> None: + # unsubscribe from the FFI queue once the listen task ends. + # disconnect() unsubscribes too, but it early-returns when the + # room is already disconnected (e.g. removed remotely). + ffi_queue = self._ffi_queue + self._task.add_done_callback(lambda _: FfiClient.instance.queue.unsubscribe(ffi_queue)) + + # Unblock the FFI server once this SDK is ready to receive room events. + ready_req = proto_ffi.FfiRequest() + ready_req.ready_for_room_event.room_handle = self._ffi_handle.handle + FfiClient.instance.request(ready_req) + + async def get_rtc_stats(self) -> RtcStats: if not self.isconnected(): - return + raise RuntimeError("the room isn't connected") req = proto_ffi.FfiRequest() - req.disconnect.room_handle = self._ffi_handle.handle # type: ignore + req.get_session_stats.room_handle = self._ffi_handle.handle # type: ignore queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) - await queue.wait_for( - lambda e: e.disconnect.async_id == resp.disconnect.async_id + cb: proto_ffi.FfiEvent = await queue.wait_for( + lambda e: e.get_session_stats.async_id == resp.get_session_stats.async_id ) finally: FfiClient.instance.queue.unsubscribe(queue) + if cb.get_session_stats.error: + raise RuntimeError(cb.get_session_stats.error) + + publisher_stats = list(cb.get_session_stats.result.publisher_stats) + subscriber_stats = list(cb.get_session_stats.result.subscriber_stats) + + return RtcStats(publisher_stats=publisher_stats, subscriber_stats=subscriber_stats) + + def register_byte_stream_handler(self, topic: str, handler: ByteStreamHandler) -> None: + existing_handler = self._byte_stream_handlers.get(topic) + if existing_handler is None: + self._byte_stream_handlers[topic] = handler + else: + raise ValueError("byte stream handler for topic '%s' already set" % topic) + + def unregister_byte_stream_handler(self, topic: str) -> None: + if self._byte_stream_handlers.get(topic): + self._byte_stream_handlers.pop(topic) + + def register_text_stream_handler(self, topic: str, handler: TextStreamHandler) -> None: + existing_handler = self._text_stream_handlers.get(topic) + if existing_handler is None: + self._text_stream_handlers[topic] = handler + else: + raise ValueError("text stream handler for topic '%s' already set" % topic) + + def unregister_text_stream_handler(self, topic: str) -> None: + if self._text_stream_handlers.get(topic): + self._text_stream_handlers.pop(topic) + + async def simulate_scenario(self, scenario: SimulateScenarioKind.ValueType) -> None: + """Trigger a reconnection / chaos scenario for testing. + + See `SimulateScenarioKind` for the available variants. Most useful + in tests to deterministically force a Resume (signal-only reconnect + that preserves the PeerConnection and existing publications) or a + full reconnect (the SDK rebuilds the RtcSession and re-publishes + existing local tracks). + + Raises a `RuntimeError` if the SDK reports a failure. + """ + if not self.isconnected() or self._ffi_handle is None: + raise RuntimeError("simulate_scenario requires a connected room") + + req = proto_ffi.FfiRequest() + req.simulate_scenario.room_handle = self._ffi_handle.handle + req.simulate_scenario.scenario = scenario + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + cb = await queue.wait_for( + lambda e: e.simulate_scenario.async_id == resp.simulate_scenario.async_id + ) + finally: + FfiClient.instance.queue.unsubscribe(queue) + + if cb.simulate_scenario.HasField("error") and cb.simulate_scenario.error: + raise RuntimeError(f"simulate_scenario failed: {cb.simulate_scenario.error}") + + async def disconnect( + self, *, reason: DisconnectReason.ValueType = DisconnectReason.CLIENT_INITIATED + ) -> None: + """Disconnects from the room.""" + if not self.isconnected(): + return + + await self._drain_rpc_invocation_tasks() + await self._drain_data_stream_tasks() + + req = proto_ffi.FfiRequest() + req.disconnect.room_handle = self._ffi_handle.handle # type: ignore + req.disconnect.reason = reason + queue = FfiClient.instance.queue.subscribe() + try: + resp = FfiClient.instance.request(req) + await queue.wait_for(lambda e: e.disconnect.async_id == resp.disconnect.async_id) + finally: + FfiClient.instance.queue.unsubscribe(queue) + await self._task FfiClient.instance.queue.unsubscribe(self._ffi_queue) + # we should manually flip the state, since the connection could have been torn down before + # the callbacks were processed + if self._connection_state != ConnectionState.CONN_DISCONNECTED: + self.local_participant._info.disconnect_reason = reason + self._connection_state = ConnectionState.CONN_DISCONNECTED + self.emit("connection_state_changed", self._connection_state) + self.emit("disconnected", reason) + async def _listen_task(self) -> None: # listen to incoming room events while True: event = await self._ffi_queue.get() - if event.room_event.room_handle == self._ffi_handle.handle: # type: ignore + if event.WhichOneof("message") == "rpc_method_invocation": + self._on_rpc_method_invocation(event.rpc_method_invocation) + elif event.room_event.room_handle == self._ffi_handle.handle: # type: ignore if event.room_event.HasField("eos"): break @@ -257,89 +719,237 @@ async def _listen_task(self) -> None: self._room_queue.put_nowait(event) await self._room_queue.join() - def _on_room_event(self, event: proto_room.RoomEvent): + # Clean up any pending RPC invocation tasks + await self._drain_rpc_invocation_tasks() + await self._drain_data_stream_tasks() + + def _on_rpc_method_invocation(self, rpc_invocation: RpcMethodInvocationEvent) -> None: + if self._local_participant is None: + return + + if rpc_invocation.local_participant_handle == self._local_participant._ffi_handle.handle: + task = self._loop.create_task( + self._local_participant._handle_rpc_method_invocation( + rpc_invocation.invocation_id, + rpc_invocation.method, + rpc_invocation.request_id, + rpc_invocation.caller_identity, + rpc_invocation.payload, + rpc_invocation.response_timeout_ms / 1000.0, + ) + ) + self._rpc_invocation_tasks.add(task) + task.add_done_callback(self._rpc_invocation_tasks.discard) + + def _on_room_event(self, event: proto_room.RoomEvent) -> None: which = event.WhichOneof("message") if which == "participant_connected": - rparticipant = self._create_remote_participant( - event.participant_connected.info - ) + rparticipant = self._create_remote_participant(event.participant_connected.info) self.emit("participant_connected", rparticipant) elif which == "participant_disconnected": - sid = event.participant_disconnected.participant_sid - rparticipant = self.participants.pop(sid) - self.participants_by_identity.pop(rparticipant.identity) + identity = event.participant_disconnected.participant_identity + rparticipant = self._remote_participants.pop(identity) + rparticipant._info.disconnect_reason = event.participant_disconnected.disconnect_reason self.emit("participant_disconnected", rparticipant) + elif which == "participant_active": + rp = self._retrieve_remote_participant(event.participant_active.participant_identity) + if rp: + rp._info.state = proto_participant.PARTICIPANT_STATE_ACTIVE + self.emit("participant_active", rp) elif which == "local_track_published": sid = event.local_track_published.track_sid - lpublication = self.local_participant.tracks[sid] - track = lpublication.track - self.emit("local_track_published", lpublication, track) + lpublication = self.local_participant.track_publications[sid] + ltrack = lpublication.track + if ltrack is not None: + ltrack._set_room(self) + self.emit("local_track_published", lpublication, ltrack) elif which == "local_track_unpublished": + # During teardown the publication may already have been removed + # from the participant's dict by LocalParticipant.unpublish_track + # (the FFI event races that async response), so the SID can be gone + # by the time this event is dispatched. Look it up defensively and + # skip the emit when it is no longer tracked, mirroring the + # local_track_republished and remote track_unpublished handlers, + # instead of raising a KeyError that _listen_task logs as an error. sid = event.local_track_unpublished.publication_sid - lpublication = self.local_participant.tracks[sid] - self.emit("local_track_unpublished", lpublication) + unpublished = self.local_participant._track_publications.get(sid) + if unpublished is not None: + del self.local_participant._track_publications[sid] + track = unpublished.track + if track is not None: + track._set_room(None) + # Emit while `publication.track` is still set, preserving the + # pre-existing payload for callbacks. This handler is synchronous + # and emit() invokes listeners synchronously, so nulling the track + # right after still completes before any other coroutine (e.g. + # unpublish_track) can interleave. + self.emit("local_track_unpublished", unpublished) + # Mirror track_unsubscribed: drop the publication's track + # reference. This also makes unpublish_track's own _set_room(None) + # a no-op when it loses the race (its `publication._track is not + # None` guard short-circuits), avoiding a redundant clear. + if track is not None: + unpublished._track = None + else: + logging.debug("local_track_unpublished for untracked publication sid %s", sid) + elif which == "local_track_republished": + # The SDK auto-republished a local track during a full + # reconnect: the underlying Track (and its bound source) is + # preserved, but the publication and track SIDs were re-issued + # by the server. Update the existing LocalTrackPublication + # object in place so application code holding a cached + # reference continues to see current state, then rekey it + # under the new SID in the participant's publications dict. + previous_sid = event.local_track_republished.previous_sid + republished = self.local_participant._track_publications.get(previous_sid) + if republished is not None: + del self.local_participant._track_publications[previous_sid] + republished._info = event.local_track_republished.info + self.local_participant._track_publications[republished.sid] = republished + if republished.track is not None: + # Keep the local-track invariant (track.sid == publication.sid, + # set at publish_track) intact across republish, then re-push + # metadata so any attached FrameProcessor learns the new + # publication SID / credentials. _set_room with the same room + # is a no-op for the token_refreshed listener but re-fans the + # metadata to every registered AudioStream. + republished.track._info.sid = republished.sid + republished.track._set_room(self) + self.emit("local_track_republished", republished, previous_sid) + elif which == "local_track_subscribed": + sid = event.local_track_subscribed.track_sid + lpublication = self.local_participant.track_publications[sid] + if not lpublication._first_subscription.done(): + lpublication._first_subscription.set_result(None) + self.emit("local_track_subscribed", lpublication.track) elif which == "track_published": - rparticipant = self.participants[event.track_published.participant_sid] - rpublication = RemoteTrackPublication(event.track_published.publication) - rparticipant.tracks[rpublication.sid] = rpublication - self.emit("track_published", rpublication, rparticipant) + # The participant may already have been removed by a racing + # disconnect or a duplicate event, so the lookup is done defensively + # and the emit is skipped when the participant is gone, mirroring the + # track_unpublished handler, instead of raising a KeyError that + # _listen_task logs as an error. + identity = event.track_published.participant_identity + published_participant = self._remote_participants.get(identity) + if published_participant is not None: + published = RemoteTrackPublication(event.track_published.publication) + published_participant._track_publications[published.sid] = published + self.emit("track_published", published, published_participant) + else: + logger.debug("track_published for untracked participant %s", identity) elif which == "track_unpublished": - rparticipant = self.participants[event.track_unpublished.participant_sid] - rpublication = rparticipant.tracks.pop( - event.track_unpublished.publication_sid - ) - self.emit("track_unpublished", rpublication, rparticipant) + # The participant or publication may already have been removed by a + # racing disconnect or a duplicate event, so both lookups are done + # defensively and the emit is skipped when the entry is gone, + # mirroring the local_track_unpublished handler, instead of raising a + # KeyError that _listen_task logs as an error. + identity = event.track_unpublished.participant_identity + sid = event.track_unpublished.publication_sid + rp = self._remote_participants.get(identity) + if rp is not None: + rpub = rp._track_publications.pop(sid, None) + if rpub is not None: + self.emit("track_unpublished", rpub, rp) + else: + logger.debug("track_unpublished for untracked publication sid %s", sid) + else: + logger.debug("track_unpublished for untracked participant %s", identity) elif which == "track_subscribed": + # The participant or publication may already have been removed by a + # racing disconnect or unpublish, so both lookups are done + # defensively and the emit is skipped when either entry is gone, + # mirroring the track_unpublished handler, instead of raising a + # KeyError that _listen_task logs as an error. owned_track_info = event.track_subscribed.track track_info = owned_track_info.info - rparticipant = self.participants[event.track_subscribed.participant_sid] - rpublication = rparticipant.tracks[track_info.sid] - rpublication.subscribed = True - if track_info.kind == TrackKind.KIND_VIDEO: - remote_video_track = RemoteVideoTrack(owned_track_info) - rpublication.track = remote_video_track - self.emit( - "track_subscribed", remote_video_track, rpublication, rparticipant - ) - elif track_info.kind == TrackKind.KIND_AUDIO: - remote_audio_track = RemoteAudioTrack(owned_track_info) - rpublication.track = remote_audio_track - self.emit( - "track_subscribed", remote_audio_track, rpublication, rparticipant + identity = event.track_subscribed.participant_identity + subscribed_participant = self._remote_participants.get(identity) + subscribed = ( + subscribed_participant.track_publications.get(track_info.sid) + if subscribed_participant is not None + else None + ) + if subscribed_participant is not None and subscribed is not None: + subscribed._subscribed = True + if track_info.kind == TrackKind.KIND_VIDEO: + remote_video_track = RemoteVideoTrack(owned_track_info) + remote_video_track._set_room(self) + subscribed._track = remote_video_track + self.emit( + "track_subscribed", remote_video_track, subscribed, subscribed_participant + ) + elif track_info.kind == TrackKind.KIND_AUDIO: + remote_audio_track = RemoteAudioTrack(owned_track_info) + remote_audio_track._set_room(self) + subscribed._track = remote_audio_track + self.emit( + "track_subscribed", remote_audio_track, subscribed, subscribed_participant + ) + else: + logger.debug( + "track_subscribed for untracked participant %s or publication %s", + identity, + track_info.sid, ) elif which == "track_unsubscribed": - sid = event.track_unsubscribed.participant_sid - rparticipant = self.participants[sid] - rpublication = rparticipant.tracks[event.track_unsubscribed.track_sid] - track = rpublication.track - rpublication.track = None - rpublication.subscribed = False - self.emit("track_unsubscribed", track, rpublication, rparticipant) - elif which == "track_subscription_failed": - sid = event.track_subscription_failed.participant_sid - rparticipant = self.participants[sid] - error = event.track_subscription_failed.error - self.emit( - "track_subscription_failed", - rparticipant, - event.track_subscription_failed.track_sid, - error, + # The participant or publication may already have been removed by a + # racing disconnect or unpublish, so both lookups are done + # defensively and the emit is skipped when either entry is gone, + # mirroring the track_unpublished handler, instead of raising a + # KeyError that _listen_task logs as an error. + identity = event.track_unsubscribed.participant_identity + unsubscribed_participant = self._remote_participants.get(identity) + unsubscribed = ( + unsubscribed_participant.track_publications.get(event.track_unsubscribed.track_sid) + if unsubscribed_participant is not None + else None ) + if unsubscribed_participant is not None and unsubscribed is not None: + rtrack = unsubscribed.track + if rtrack is not None: + rtrack._set_room(None) + unsubscribed._track = None + unsubscribed._subscribed = False + self.emit("track_unsubscribed", rtrack, unsubscribed, unsubscribed_participant) + else: + logger.debug( + "track_unsubscribed for untracked participant %s or publication %s", + identity, + event.track_unsubscribed.track_sid, + ) + elif which == "track_subscription_failed": + # The participant may already have been removed by a racing + # disconnect, so the lookup is done defensively and the emit is + # skipped when the participant is gone, instead of raising a KeyError + # that _listen_task logs as an error. + identity = event.track_subscription_failed.participant_identity + failed_participant = self._remote_participants.get(identity) + if failed_participant is not None: + error = event.track_subscription_failed.error + self.emit( + "track_subscription_failed", + failed_participant, + event.track_subscription_failed.track_sid, + error, + ) + else: + logger.debug("track_subscription_failed for untracked participant %s", identity) elif which == "track_muted": - sid = event.track_muted.participant_sid + identity = event.track_muted.participant_identity # TODO: pass participant identity - participant = self._retrieve_participant(sid, "") - publication = participant.tracks[event.track_muted.track_sid] + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) + publication = participant.track_publications[event.track_muted.track_sid] publication._info.muted = True if publication.track: publication.track._info.muted = True self.emit("track_muted", participant, publication) elif which == "track_unmuted": - sid = event.track_unmuted.participant_sid + identity = event.track_unmuted.participant_identity # TODO: pass participant identity - participant = self._retrieve_participant(sid, "") - publication = participant.tracks[event.track_unmuted.track_sid] + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) + publication = participant.track_publications[event.track_unmuted.track_sid] publication._info.muted = False if publication.track: publication.track._info.muted = False @@ -348,18 +958,25 @@ def _on_room_event(self, event: proto_room.RoomEvent): elif which == "active_speakers_changed": speakers: list[Participant] = [] # TODO: pass participant identity - for sid in event.active_speakers_changed.participant_sids: - speakers.append(self._retrieve_participant(sid, "")) + for identity in event.active_speakers_changed.participant_identities: + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) + speakers.append(participant) self.emit("active_speakers_changed", speakers) elif which == "room_metadata_changed": old_metadata = self.metadata self._info.metadata = event.room_metadata_changed.metadata self.emit("room_metadata_changed", old_metadata, self.metadata) + elif which == "room_sid_changed": + self._resolve_first_sid(event.room_sid_changed.sid) + self._info.sid = event.room_sid_changed.sid + # This is an internal event, not exposed to users elif which == "participant_metadata_changed": - sid = event.participant_metadata_changed.participant_sid + identity = event.participant_metadata_changed.participant_identity # TODO: pass participant identity - participant = self._retrieve_participant(sid, "") + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) old_metadata = participant.metadata participant._info.metadata = event.participant_metadata_changed.metadata self.emit( @@ -369,23 +986,75 @@ def _on_room_event(self, event: proto_room.RoomEvent): participant.metadata, ) elif which == "participant_name_changed": - sid = event.participant_name_changed.participant_sid - # TODO: pass participant identity - participant = self._retrieve_participant(sid, "") + identity = event.participant_name_changed.participant_identity + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) old_name = participant.name participant._info.name = event.participant_name_changed.name + self.emit("participant_name_changed", participant, old_name, participant.name) + elif which == "participant_attributes_changed": + identity = event.participant_attributes_changed.participant_identity + attributes = event.participant_attributes_changed.attributes + changed_attributes = dict( + (entry.key, entry.value) + for entry in event.participant_attributes_changed.changed_attributes + ) + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) + participant._info.attributes.clear() + participant._info.attributes.update((entry.key, entry.value) for entry in attributes) + self.emit( + "participant_attributes_changed", + changed_attributes, + participant, + ) + elif which == "participant_encryption_status_changed": + identity = event.participant_encryption_status_changed.participant_identity + participant = self._retrieve_participant(identity) self.emit( - "participant_name_changed", participant, old_name, participant.name + "participant_encryption_status_changed", + participant, + event.participant_encryption_status_changed.is_encrypted, + ) + elif which == "participant_permission_changed": + identity = event.participant_permission_changed.participant_identity + participant = self._retrieve_participant(identity) + assert isinstance(participant, Participant) + participant._info.permission.CopyFrom(event.participant_permission_changed.permission) + self.emit( + "participant_permissions_changed", + participant, + participant.permissions, ) elif which == "connection_quality_changed": - sid = event.connection_quality_changed.participant_sid - # TODO: pass participant identity - participant = self._retrieve_participant(sid, "") + identity = event.connection_quality_changed.participant_identity + participant = self._retrieve_participant(identity) + quality = _connection_quality_from_proto(event.connection_quality_changed.quality) + if participant: + participant._connection_quality = quality self.emit( "connection_quality_changed", participant, - event.connection_quality_changed.quality, + quality, ) + elif which == "transcription_received": + transcription = event.transcription_received + segments = [ + TranscriptionSegment( + id=s.id, + text=s.text, + final=s.final, + start_time=s.start_time, + end_time=s.end_time, + language=s.language, + ) + for s in transcription.segments + ] + part = self._retrieve_participant(transcription.participant_identity) + pub: TrackPublication | None = None + if part: + pub = part.track_publications.get(transcription.track_sid) + self.emit("transcription_received", segments, part, pub) elif which == "data_packet_received": packet = event.data_packet_received which_val = packet.WhichOneof("value") @@ -398,9 +1067,10 @@ def _on_room_event(self, event: proto_room.RoomEvent): ).contents data = bytes(native_data) - FfiHandle(owned_buffer_info.handle.id) - rparticipant = self._retrieve_remote_participant( - packet.participant_sid, packet.participant_identity + FfiHandle(owned_buffer_info.handle.id).dispose() + rparticipant = cast( + RemoteParticipant, + self._retrieve_remote_participant(packet.participant_identity), ) self.emit( "data_received", @@ -412,8 +1082,9 @@ def _on_room_event(self, event: proto_room.RoomEvent): ), ) elif which_val == "sip_dtmf": - rparticipant = self._retrieve_remote_participant( - packet.participant_sid, packet.participant_identity + rparticipant = cast( + RemoteParticipant, + self._retrieve_remote_participant(packet.participant_identity), ) self.emit( "sip_dtmf_received", @@ -423,57 +1094,153 @@ def _on_room_event(self, event: proto_room.RoomEvent): participant=rparticipant, ), ) - elif which == "e2ee_state_changed": - sid = event.e2ee_state_changed.participant_sid + identity = event.e2ee_state_changed.participant_identity e2ee_state = event.e2ee_state_changed.state # TODO: pass participant identity - self.emit( - "e2ee_state_changed", self._retrieve_participant(sid, ""), e2ee_state - ) + self.emit("e2ee_state_changed", self._retrieve_participant(identity), e2ee_state) elif which == "connection_state_changed": connection_state = event.connection_state_changed.state - self.connection_state = connection_state + self._connection_state = connection_state self.emit("connection_state_changed", connection_state) - elif which == "connected": - self.emit("connected") elif which == "disconnected": - self.emit("disconnected") + self.emit("disconnected", event.disconnected.reason) elif which == "reconnecting": self.emit("reconnecting") elif which == "reconnected": self.emit("reconnected") + elif which == "stream_header_received": + self._handle_stream_header( + event.stream_header_received.header, + event.stream_header_received.participant_identity, + ) + elif which == "stream_chunk_received": + task = asyncio.create_task(self._handle_stream_chunk(event.stream_chunk_received.chunk)) + self._data_stream_tasks.add(task) + task.add_done_callback(self._data_stream_tasks.discard) - def _retrieve_remote_participant( - self, sid: str, identity: str - ) -> RemoteParticipant: - """Retrieve a remote participant by sid or identity""" - participant = None - if identity: - participant = self.participants_by_identity[identity] - if not participant: - participant = self.participants[sid] - return participant + elif which == "stream_trailer_received": + task = asyncio.create_task( + self._handle_stream_trailer(event.stream_trailer_received.trailer) + ) + self._data_stream_tasks.add(task) + task.add_done_callback(self._data_stream_tasks.discard) + + elif which == "room_updated": + self._info = event.room_updated + self._resolve_first_sid(self._info.sid) + self.emit("room_updated") + + elif which == "moved": + self._info = event.moved + self._resolve_first_sid(self._info.sid) + self.emit("moved") + + elif which == "participants_updated": + for info in event.participants_updated.participants: + participant = self._retrieve_participant(info.identity) + if participant: + participant._info = info + + elif which == "token_refreshed": + self._token = event.token_refreshed.token + self.emit("token_refreshed") - def _retrieve_participant(self, sid: str, identity: str) -> Participant: - """Retrieve a participant by sid or identity, - returns the LocalParticipant if sid or identity matches""" + elif which == "data_track_published": + remote_data_track = RemoteDataTrack(event.data_track_published.track) + self.emit("data_track_published", remote_data_track) + + elif which == "data_track_unpublished": + self.emit("data_track_unpublished", event.data_track_unpublished.sid) + + def _handle_stream_header( + self, header: proto_room.DataStream.Header, participant_identity: str + ) -> None: + stream_type = header.WhichOneof("content_header") + if stream_type == "text_header": + text_stream_handler = self._text_stream_handlers.get(header.topic) + if text_stream_handler is None: + logging.info( + "ignoring text stream with topic '%s', no callback attached", + header.topic, + ) + return + + text_reader = TextStreamReader(header) + self._text_stream_readers[header.stream_id] = text_reader + text_stream_handler(text_reader, participant_identity) + elif stream_type == "byte_header": + byte_stream_handler = self._byte_stream_handlers.get(header.topic) + if byte_stream_handler is None: + logging.info( + "ignoring byte stream with topic '%s', no callback attached", + header.topic, + ) + return + + byte_reader = ByteStreamReader(header) + self._byte_stream_readers[header.stream_id] = byte_reader + byte_stream_handler(byte_reader, participant_identity) + else: + logging.warning("received unknown header type, %s", stream_type) + pass + + async def _handle_stream_chunk(self, chunk: proto_room.DataStream.Chunk) -> None: + text_reader = self._text_stream_readers.get(chunk.stream_id) + file_reader = self._byte_stream_readers.get(chunk.stream_id) + + if text_reader: + await text_reader._on_chunk_update(chunk) + elif file_reader: + await file_reader._on_chunk_update(chunk) + + async def _handle_stream_trailer(self, trailer: proto_room.DataStream.Trailer) -> None: + text_reader = self._text_stream_readers.get(trailer.stream_id) + file_reader = self._byte_stream_readers.get(trailer.stream_id) + + if text_reader: + await text_reader._on_stream_close(trailer) + self._text_stream_readers.pop(trailer.stream_id) + elif file_reader: + await file_reader._on_stream_close(trailer) + self._byte_stream_readers.pop(trailer.stream_id) + + async def _drain_rpc_invocation_tasks(self) -> None: + if self._rpc_invocation_tasks: + for task in self._rpc_invocation_tasks: + task.cancel() + await asyncio.gather(*self._rpc_invocation_tasks, return_exceptions=True) + + async def _drain_data_stream_tasks(self) -> None: + if self._data_stream_tasks: + for task in self._data_stream_tasks: + task.cancel() + await asyncio.gather(*self._data_stream_tasks, return_exceptions=True) + + def _retrieve_remote_participant(self, identity: str) -> Optional[RemoteParticipant]: + """Retrieve a remote participant by identity""" + return self._remote_participants.get(identity, None) + + def _retrieve_participant(self, identity: str) -> Optional[Participant]: + """Retrieve a local or remote participant by identity""" if identity and identity == self.local_participant.identity: return self.local_participant - if sid and sid == self.local_participant.sid: - return self.local_participant - else: - return self._retrieve_remote_participant(sid, identity) + + return self._retrieve_remote_participant(identity) def _create_remote_participant( self, owned_info: proto_participant.OwnedParticipant ) -> RemoteParticipant: - if owned_info.info.sid in self.participants: - raise Exception("participant already exists") - if owned_info.info.identity in self.participants_by_identity: + if owned_info.info.identity in self._remote_participants: raise Exception("participant already exists") participant = RemoteParticipant(owned_info) - self.participants[participant.sid] = participant - self.participants_by_identity[participant.identity] = participant + self._remote_participants[participant.identity] = participant return participant + + def __repr__(self) -> str: + sid = "unknown" + if self._first_sid_future.done(): + sid = self._first_sid_future.result() + + return f"rtc.Room(sid={sid}, name={self.name}, metadata={self.metadata}, connection_state={ConnectionState.Name(self._connection_state)})" diff --git a/livekit-rtc/livekit/rtc/rpc.py b/livekit-rtc/livekit/rtc/rpc.py new file mode 100644 index 00000000..02a5b7f4 --- /dev/null +++ b/livekit-rtc/livekit/rtc/rpc.py @@ -0,0 +1,122 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from typing import Optional, Dict, Union, ClassVar +from enum import IntEnum +from ._proto import rpc_pb2 as proto_rpc +from dataclasses import dataclass + + +@dataclass +class RpcInvocationData: + """Data passed to method handler for incoming RPC invocations + + Attributes: + request_id (str): The unique request ID. Will match at both sides of the call, useful for debugging or logging. + caller_identity (str): The unique participant identity of the caller. + payload (str): The payload of the request. User-definable format, typically JSON. + response_timeout (float): The maximum time the caller will wait for a response. + """ + + request_id: str + caller_identity: str + payload: str + response_timeout: float + + +class RpcError(Exception): + """ + Specialized error handling for RPC methods. + + Instances of this type, when thrown in a method handler, will have their `message` + serialized and sent across the wire. The caller will receive an equivalent error on the other side. + + Built-in errors are included (codes 1001-1999) but developers may use the code, message, and data fields to create their own errors. + """ + + class ErrorCode(IntEnum): + APPLICATION_ERROR = 1500 + CONNECTION_TIMEOUT = 1501 + RESPONSE_TIMEOUT = 1502 + RECIPIENT_DISCONNECTED = 1503 + RESPONSE_PAYLOAD_TOO_LARGE = 1504 + SEND_FAILED = 1505 + + UNSUPPORTED_METHOD = 1400 + RECIPIENT_NOT_FOUND = 1401 + REQUEST_PAYLOAD_TOO_LARGE = 1402 + UNSUPPORTED_SERVER = 1403 + UNSUPPORTED_VERSION = 1404 + + ErrorMessage: ClassVar[Dict[ErrorCode, str]] = { + ErrorCode.APPLICATION_ERROR: "Application error in method handler", + ErrorCode.CONNECTION_TIMEOUT: "Connection timeout", + ErrorCode.RESPONSE_TIMEOUT: "Response timeout", + ErrorCode.RECIPIENT_DISCONNECTED: "Recipient disconnected", + ErrorCode.RESPONSE_PAYLOAD_TOO_LARGE: "Response payload too large", + ErrorCode.SEND_FAILED: "Failed to send", + ErrorCode.UNSUPPORTED_METHOD: "Method not supported at destination", + ErrorCode.RECIPIENT_NOT_FOUND: "Recipient not found", + ErrorCode.REQUEST_PAYLOAD_TOO_LARGE: "Request payload too large", + ErrorCode.UNSUPPORTED_SERVER: "RPC not supported by server", + ErrorCode.UNSUPPORTED_VERSION: "Unsupported RPC version", + } + + def __init__( + self, + code: Union[int, "RpcError.ErrorCode"], + message: str, + data: Optional[str] = None, + ): + """ + Creates an error object with the given code and message, plus an optional data payload. + + If thrown in an RPC method handler, the error will be sent back to the caller. + + Args: + code (int): Your error code (Error codes 1001-1999 are reserved for built-in errors) + message (str): A readable error message. + data (Optional[str]): Optional additional data associated with the error (JSON recommended) + """ + super().__init__(message) + self._code = code + self._message = message + self._data = data + + @property + def code(self) -> int: + """Error code value. Codes 1001-1999 are reserved for built-in errors (see RpcError.ErrorCode for their meanings).""" + return self._code + + @property + def message(self) -> str: + """A readable error message.""" + return self._message + + @property + def data(self) -> Optional[str]: + """Optional additional data associated with the error (JSON recommended).""" + return self._data + + @classmethod + def _from_proto(cls, proto: proto_rpc.RpcError) -> "RpcError": + return cls(proto.code, proto.message, proto.data) + + def _to_proto(self) -> proto_rpc.RpcError: + return proto_rpc.RpcError(code=self.code, message=self.message, data=self.data) + + @classmethod + def _built_in(cls, code: "RpcError.ErrorCode", data: Optional[str] = None) -> "RpcError": + message = cls.ErrorMessage[code] + return cls(code, message, data) diff --git a/livekit-rtc/livekit/rtc/synchronizer.py b/livekit-rtc/livekit/rtc/synchronizer.py new file mode 100644 index 00000000..813a3641 --- /dev/null +++ b/livekit-rtc/livekit/rtc/synchronizer.py @@ -0,0 +1,202 @@ +import asyncio +import logging +import time +from collections import deque +from typing import Any, Optional, Union + +from .video_frame import VideoFrame +from .audio_frame import AudioFrame +from .audio_source import AudioSource +from .video_source import VideoSource + + +logger = logging.getLogger(__name__) + + +class AVSynchronizer: + """Synchronize audio and video capture. + + Usage: + av_sync = AVSynchronizer( + audio_source=audio_source, + video_source=video_source, + video_fps=video_fps, + ) + + async for video_frame, audio_frame in video_generator: + await av_sync.push(video_frame) + await av_sync.push(audio_frame) + """ + + def __init__( + self, + *, + audio_source: AudioSource, + video_source: VideoSource, + video_fps: float, + video_queue_size_ms: float = 100, + _max_delay_tolerance_ms: float = 300, + ): + self._audio_source = audio_source + self._video_source = video_source + self._video_fps = video_fps + self._video_queue_size_ms = video_queue_size_ms + self._max_delay_tolerance_ms = _max_delay_tolerance_ms + + self._stopped = False + # the time of the last video/audio frame captured + self._last_video_time: float = 0 + self._last_audio_time: float = 0 + + self._video_queue_max_size = int(self._video_fps * self._video_queue_size_ms / 1000) + if self._video_queue_size_ms > 0: + # ensure queue is bounded if queue size is specified + self._video_queue_max_size = max(1, self._video_queue_max_size) + + self._video_queue = asyncio.Queue[tuple[VideoFrame, Optional[float]]]( + maxsize=self._video_queue_max_size + ) + self._fps_controller = _FPSController( + expected_fps=self._video_fps, + max_delay_tolerance_ms=self._max_delay_tolerance_ms, + ) + self._capture_video_task = asyncio.create_task(self._capture_video()) + + async def push( + self, frame: Union[VideoFrame, AudioFrame], timestamp: Optional[float] = None + ) -> None: + """Push a frame to the synchronizer + + Args: + frame: The video or audio frame to push. + timestamp: (optional) The timestamp of the frame, for logging purposes for now. + For AudioFrame, it should be the end time of the frame. + """ + if isinstance(frame, AudioFrame): + await self._audio_source.capture_frame(frame) + if timestamp is not None: + self._last_audio_time = timestamp + return + + await self._video_queue.put((frame, timestamp)) + + async def clear_queue(self) -> None: + self._audio_source.clear_queue() + while not self._video_queue.empty(): + await self._video_queue.get() + self._video_queue.task_done() + + async def wait_for_playout(self) -> None: + """Wait until all video and audio frames are played out.""" + await asyncio.gather( + self._audio_source.wait_for_playout(), + self._video_queue.join(), + ) + + def reset(self) -> None: + self._fps_controller.reset() + + async def _capture_video(self) -> None: + while not self._stopped: + frame, timestamp = await self._video_queue.get() + async with self._fps_controller: + self._video_source.capture_frame(frame) + if timestamp is not None: + self._last_video_time = timestamp + self._video_queue.task_done() + + async def aclose(self) -> None: + self._stopped = True + if self._capture_video_task: + self._capture_video_task.cancel() + + @property + def actual_fps(self) -> float: + return self._fps_controller.actual_fps + + @property + def last_video_time(self) -> float: + """The time of the last video frame captured""" + return self._last_video_time + + @property + def last_audio_time(self) -> float: + """The time of the last audio frame played out""" + return self._last_audio_time - self._audio_source.queued_duration + + +class _FPSController: + def __init__(self, *, expected_fps: float, max_delay_tolerance_ms: float = 300) -> None: + """Controls frame rate by adjusting sleep time based on actual FPS. + + Usage: + async with _FPSController(expected_fps=30): + # process frame + pass + + Args: + expected_fps: Target frames per second + max_delay_tolerance_ms: Maximum delay tolerance in milliseconds + """ + self._expected_fps = expected_fps + self._frame_interval = 1.0 / expected_fps + self._max_delay_tolerance_secs = max_delay_tolerance_ms / 1000 + + self._next_frame_time: Optional[float] = None + self._fps_calc_winsize = max(2, int(1.0 * expected_fps)) + self._send_timestamps: deque[float] = deque(maxlen=self._fps_calc_winsize) + + async def __aenter__(self) -> None: + await self.wait_next_process() + + async def __aexit__(self, exc_type: Any, exc_val: Any, exc_tb: Any) -> None: + self.after_process() + + def reset(self) -> None: + self._next_frame_time = None + self._send_timestamps.clear() + + async def wait_next_process(self) -> None: + """Wait until it's time for the next frame. + + Adjusts sleep time based on actual FPS to maintain target rate. + """ + current_time = time.perf_counter() + + # initialize the next frame time + if self._next_frame_time is None: + self._next_frame_time = current_time + + # calculate sleep time + sleep_time = self._next_frame_time - current_time + if sleep_time > 0: + await asyncio.sleep(sleep_time) + else: + # check if significantly behind schedule + if -sleep_time > self._max_delay_tolerance_secs: + logger.warning(f"Frame capture was behind schedule for {-sleep_time * 1000:.2f} ms") + self._next_frame_time = time.perf_counter() + + def after_process(self) -> None: + """Update timing information after processing a frame.""" + assert self._next_frame_time is not None, "wait_next_process must be called first" + + # update timing information + self._send_timestamps.append(time.perf_counter()) + + # calculate next frame time + self._next_frame_time += self._frame_interval + + @property + def expected_fps(self) -> float: + return self._expected_fps + + @property + def actual_fps(self) -> float: + """Get current average FPS.""" + if len(self._send_timestamps) < 2: + return 0 + + return (len(self._send_timestamps) - 1) / ( + self._send_timestamps[-1] - self._send_timestamps[0] + ) diff --git a/livekit-rtc/livekit/rtc/track.py b/livekit-rtc/livekit/rtc/track.py index abfe1adc..684e2493 100644 --- a/livekit-rtc/livekit/rtc/track.py +++ b/livekit-rtc/livekit/rtc/track.py @@ -12,7 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import TYPE_CHECKING, List, Union +from __future__ import annotations + +import weakref +from typing import TYPE_CHECKING, List, Optional, Union from ._ffi_client import FfiHandle, FfiClient from ._proto import ffi_pb2 as proto_ffi from ._proto import track_pb2 as proto_track @@ -20,13 +23,93 @@ if TYPE_CHECKING: from .audio_source import AudioSource + from .audio_stream import AudioStream + from .room import Room from .video_source import VideoSource + from .platform_audio import PlatformAudioSource class Track: def __init__(self, owned_info: proto_track.OwnedTrack): self._info = owned_info.info self._ffi_handle = FfiHandle(owned_info.handle.id) + self._room_ref: Optional[weakref.ref[Room]] = None + self._audio_streams: weakref.WeakSet[AudioStream] = weakref.WeakSet() + + def _resolve_room(self) -> Optional[Room]: + return self._room_ref() if self._room_ref is not None else None + + def _set_room(self, room: Optional[Room]) -> None: + old_room = self._resolve_room() + if old_room is None and room is None: + # Already roomless — nothing to detach and nothing to re-clear. + # Without this guard a second _set_room(None) (e.g. the unpublish / + # local_track_unpublished race calling it from both paths) would + # re-fire _on_*_cleared on every registered processor. + return + if old_room is not room: + if old_room is not None: + old_room.off("token_refreshed", self._on_room_token_refreshed) + if room is not None: + room.on("token_refreshed", self._on_room_token_refreshed) + + self._room_ref = weakref.ref(room) if room is not None else None + + for stream in self._audio_streams: + self._push_processor_metadata_to_stream(stream, room) + + def _on_room_token_refreshed(self) -> None: + room = self._resolve_room() + if room is None or room._token is None or room._server_url is None: + return + for stream in self._audio_streams: + if not stream._processor: + continue + stream._processor._on_credentials_updated(token=room._token, url=room._server_url) + + def _push_processor_metadata_to_stream(self, stream: AudioStream, room: Optional[Room]) -> None: + if not stream._processor: + return + + if room is None: + # track left a room - clear processor's room context + stream._processor._on_stream_info_cleared() + stream._processor._on_credentials_cleared() + return + + identity = "" + pub_sid = "" + track_sid = self.sid + if track_sid: + for participant in room.remote_participants.values(): + publication = participant.track_publications.get(track_sid) + if publication is not None: + identity, pub_sid = participant.identity, publication.sid + break + else: + local = room._local_participant + if local is not None: + for local_publication in local.track_publications.values(): + if local_publication.sid == track_sid: + identity, pub_sid = local.identity, local_publication.sid + break + + stream._processor._on_stream_info_updated( + room_name=room.name, + participant_identity=identity, + publication_sid=pub_sid, + ) + if room._token is not None and room._server_url is not None: + stream._processor._on_credentials_updated(token=room._token, url=room._server_url) + + def _register_audio_stream(self, stream: AudioStream) -> None: + self._audio_streams.add(stream) + room = self._resolve_room() + if room is not None: + self._push_processor_metadata_to_stream(stream, room) + + def _unregister_audio_stream(self, stream: AudioStream) -> None: + self._audio_streams.discard(stream) @property def sid(self) -> str: @@ -55,7 +138,7 @@ async def get_stats(self) -> List[proto_stats.RtcStats]: queue = FfiClient.instance.queue.subscribe() try: resp = FfiClient.instance.request(req) - cb = await queue.wait_for( + cb: proto_ffi.FfiEvent = await queue.wait_for( lambda e: e.get_stats.async_id == resp.get_stats.async_id ) finally: @@ -72,7 +155,34 @@ def __init__(self, info: proto_track.OwnedTrack): super().__init__(info) @staticmethod - def create_audio_track(name: str, source: "AudioSource") -> "LocalAudioTrack": + def create_audio_track( + name: str, source: Union["AudioSource", "PlatformAudioSource"] + ) -> "LocalAudioTrack": + """Create a local audio track from an audio source. + + Args: + name: The name of the track (e.g., "microphone", "audio-file"). + source: Either an AudioSource (synthetic mode for manual frame capture) + or a PlatformAudioSource (from PlatformAudio, uses WebRTC ADM). + + Returns: + A LocalAudioTrack that can be published to a room. + + Example with PlatformAudio (recommended for most use cases): + ```python + platform_audio = rtc.PlatformAudio() + source = platform_audio.create_audio_source() + track = rtc.LocalAudioTrack.create_audio_track("microphone", source) + ``` + + Example with AudioSource (synthetic mode for custom processing): + ```python + # Synthetic mode: You must manually capture frames + source = rtc.AudioSource(sample_rate=48000, num_channels=1) + track = rtc.LocalAudioTrack.create_audio_track("audio", source) + # Then in a loop: await source.capture_frame(frame) + ``` + """ req = proto_ffi.FfiRequest() req.create_audio_track.name = name req.create_audio_track.source_handle = source._ffi_handle.handle @@ -80,6 +190,23 @@ def create_audio_track(name: str, source: "AudioSource") -> "LocalAudioTrack": resp = FfiClient.instance.request(req) return LocalAudioTrack(resp.create_audio_track.track) + def mute(self) -> None: + req = proto_ffi.FfiRequest() + req.local_track_mute.track_handle = self._ffi_handle.handle + req.local_track_mute.mute = True + FfiClient.instance.request(req) + self._info.muted = True + + def unmute(self) -> None: + req = proto_ffi.FfiRequest() + req.local_track_mute.track_handle = self._ffi_handle.handle + req.local_track_mute.mute = False + FfiClient.instance.request(req) + self._info.muted = False + + def __repr__(self) -> str: + return f"rtc.LocalAudioTrack(sid={self.sid}, name={self.name})" + class LocalVideoTrack(Track): def __init__(self, info: proto_track.OwnedTrack): @@ -94,16 +221,39 @@ def create_video_track(name: str, source: "VideoSource") -> "LocalVideoTrack": resp = FfiClient.instance.request(req) return LocalVideoTrack(resp.create_video_track.track) + def mute(self) -> None: + req = proto_ffi.FfiRequest() + req.local_track_mute.track_handle = self._ffi_handle.handle + req.local_track_mute.mute = True + FfiClient.instance.request(req) + self._info.muted = True + + def unmute(self) -> None: + req = proto_ffi.FfiRequest() + req.local_track_mute.track_handle = self._ffi_handle.handle + req.local_track_mute.mute = False + FfiClient.instance.request(req) + self._info.muted = False + + def __repr__(self) -> str: + return f"rtc.LocalVideoTrack(sid={self.sid}, name={self.name})" + class RemoteAudioTrack(Track): def __init__(self, info: proto_track.OwnedTrack): super().__init__(info) + def __repr__(self) -> str: + return f"rtc.RemoteAudioTrack(sid={self.sid}, name={self.name})" + class RemoteVideoTrack(Track): def __init__(self, info: proto_track.OwnedTrack): super().__init__(info) + def __repr__(self) -> str: + return f"rtc.RemoteVideoTrack(sid={self.sid}, name={self.name})" + LocalTrack = Union[LocalVideoTrack, LocalAudioTrack] RemoteTrack = Union[RemoteVideoTrack, RemoteAudioTrack] diff --git a/livekit-rtc/livekit/rtc/track_publication.py b/livekit-rtc/livekit/rtc/track_publication.py index 104acda8..2adbd67b 100644 --- a/livekit-rtc/livekit/rtc/track_publication.py +++ b/livekit-rtc/livekit/rtc/track_publication.py @@ -12,21 +12,29 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional +from typing import List, Optional, cast +import asyncio from ._ffi_client import FfiHandle, FfiClient from ._proto import e2ee_pb2 as proto_e2ee from ._proto import ffi_pb2 as proto_ffi from ._proto import track_pb2 as proto_track -from .track import Track +from ._proto import track_publication_pb2 as proto_track_pub +from .track import Track, LocalTrack, RemoteTrack + +VideoQuality = proto_track_pub.VideoQuality class TrackPublication: def __init__(self, owned_info: proto_track.OwnedTrackPublication): self._info = owned_info.info - self.track: Optional[Track] = None + self._track: Optional[Track] = None self._ffi_handle = FfiHandle(owned_info.handle.id) + @property + def track(self) -> Optional[Track]: + return self._track + @property def sid(self) -> str: return self._info.sid @@ -67,19 +75,71 @@ def muted(self) -> bool: def encryption_type(self) -> proto_e2ee.EncryptionType.ValueType: return self._info.encryption_type + @property + def audio_features(self) -> List[proto_track.AudioTrackFeature.ValueType]: + return list(self._info.audio_features) + + @property + def frame_metadata_features(self) -> List[proto_track.FrameMetadataFeature.ValueType]: + return list(self._info.frame_metadata_features) + class LocalTrackPublication(TrackPublication): def __init__(self, owned_info: proto_track.OwnedTrackPublication): super().__init__(owned_info) + self._first_subscription: asyncio.Future[None] = asyncio.Future() + + @property + def track(self) -> Optional[LocalTrack]: + return cast(Optional[LocalTrack], self._track) + + async def wait_for_subscription(self) -> None: + await asyncio.shield(self._first_subscription) + + def __repr__(self) -> str: + return f"rtc.LocalTrackPublication(sid={self.sid}, name={self.name}, kind={self.kind}, source={self.source})" class RemoteTrackPublication(TrackPublication): def __init__(self, owned_info: proto_track.OwnedTrackPublication): super().__init__(owned_info) - self.subscribed = False + self._subscribed = False + + @property + def track(self) -> Optional[RemoteTrack]: + return cast(Optional[RemoteTrack], self._track) + + @property + def subscribed(self) -> bool: + return self._subscribed - def set_subscribed(self, subscribed: bool): + def set_subscribed(self, subscribed: bool) -> None: req = proto_ffi.FfiRequest() req.set_subscribed.subscribe = subscribed req.set_subscribed.publication_handle = self._ffi_handle.handle FfiClient.instance.request(req) + + def set_video_quality(self, quality: "VideoQuality.ValueType") -> None: + """For simulcasted video tracks, request a specific simulcast layer + from the server. Use one of `rtc.VideoQuality.VIDEO_QUALITY_LOW` (q), + `VIDEO_QUALITY_MEDIUM` (h), or `VIDEO_QUALITY_HIGH` (f). + + Raises: + ValueError: if the publication is not a video track, or is not + simulcasted (selecting a layer requires multiple layers). + """ + if self.kind != proto_track.TrackKind.KIND_VIDEO: + raise ValueError( + f"set_video_quality is only supported on video publications (sid={self.sid})" + ) + if not self.simulcasted: + raise ValueError( + f"set_video_quality requires a simulcasted publication (sid={self.sid})" + ) + req = proto_ffi.FfiRequest() + req.set_remote_track_publication_quality.track_publication_handle = self._ffi_handle.handle + req.set_remote_track_publication_quality.quality = quality + FfiClient.instance.request(req) + + def __repr__(self) -> str: + return f"rtc.RemoteTrackPublication(sid={self.sid}, name={self.name}, kind={self.kind}, source={self.source})" diff --git a/livekit-rtc/livekit/rtc/transcription.py b/livekit-rtc/livekit/rtc/transcription.py index 3694e0d0..7403f6bd 100644 --- a/livekit-rtc/livekit/rtc/transcription.py +++ b/livekit-rtc/livekit/rtc/transcription.py @@ -5,9 +5,8 @@ @dataclass class Transcription: participant_identity: str - track_id: str + track_sid: str segments: List["TranscriptionSegment"] - language: str @dataclass @@ -16,4 +15,5 @@ class TranscriptionSegment: text: str start_time: int end_time: int + language: str final: bool diff --git a/livekit-rtc/livekit/rtc/utils.py b/livekit-rtc/livekit/rtc/utils.py new file mode 100644 index 00000000..55f7d991 --- /dev/null +++ b/livekit-rtc/livekit/rtc/utils.py @@ -0,0 +1,138 @@ +from __future__ import annotations + +from typing import AsyncIterator + +from .audio_frame import AudioFrame + + +__all__ = ["combine_audio_frames", "sine_wave_generator"] + + +def combine_audio_frames(buffer: AudioFrame | list[AudioFrame]) -> AudioFrame: + """ + Combines one or more `rtc.AudioFrame` objects into a single `rtc.AudioFrame`. + + This function concatenates the audio data from multiple frames, ensuring that + all frames have the same sample rate and number of channels. It efficiently + merges the data by preallocating the necessary memory and copying the frame + data without unnecessary reallocations. + + Args: + buffer: A single `rtc.AudioFrame` or a list of `rtc.AudioFrame` + objects to be combined. + + Returns: + rtc.AudioFrame: A new `rtc.AudioFrame` containing the combined audio data. + + Raises: + ValueError: If the buffer is empty. + ValueError: If frames have differing sample rates. + ValueError: If frames have differing numbers of channels. + + Example: + >>> frame1 = rtc.AudioFrame( + ... data=b"\x01\x02", sample_rate=48000, num_channels=2, samples_per_channel=1 + ... ) + >>> frame2 = rtc.AudioFrame( + ... data=b"\x03\x04", sample_rate=48000, num_channels=2, samples_per_channel=1 + ... ) + >>> combined_frame = combine_audio_frames([frame1, frame2]) + >>> combined_frame.data + b'\x01\x02\x03\x04' + >>> combined_frame.sample_rate + 48000 + >>> combined_frame.num_channels + 2 + >>> combined_frame.samples_per_channel + 2 + """ + if not isinstance(buffer, list): + return buffer + + if not buffer: + raise ValueError("buffer is empty") + + sample_rate = buffer[0].sample_rate + num_channels = buffer[0].num_channels + + total_data_length = 0 + total_samples_per_channel = 0 + + for frame in buffer: + if frame.sample_rate != sample_rate: + raise ValueError( + f"Sample rate mismatch: expected {sample_rate}, got {frame.sample_rate}" + ) + + if frame.num_channels != num_channels: + raise ValueError( + f"Channel count mismatch: expected {num_channels}, got {frame.num_channels}" + ) + + total_data_length += len(frame.data) + total_samples_per_channel += frame.samples_per_channel + + data = bytearray(total_data_length) + offset = 0 + for frame in buffer: + frame_data = frame.data.cast("b") + data[offset : offset + len(frame_data)] = frame_data + offset += len(frame_data) + + return AudioFrame( + data=data, + sample_rate=sample_rate, + num_channels=num_channels, + samples_per_channel=total_samples_per_channel, + ) + + +async def sine_wave_generator( + freq: float, + duration: float, + sample_rate: int = 48000, + amplitude: float = 0.3, +) -> AsyncIterator[AudioFrame]: + """ + Generate sine wave audio frames. + + Useful for testing audio pipelines and generating test signals. + + Args: + freq: Frequency of the sine wave in Hz. + duration: Duration of the audio in seconds. + sample_rate: Sample rate in Hz (default: 48000). + amplitude: Amplitude of the sine wave, range [0.0, 1.0] (default: 0.3). + + Yields: + AudioFrame: Audio frames containing sine wave data. + + Example: + >>> import asyncio + >>> async def generate_audio(): + ... async for frame in sine_wave_generator(440, 1.0): + ... print(f"Generated frame with {frame.samples_per_channel} samples") + >>> asyncio.run(generate_audio()) + """ + try: + import numpy as np + except ImportError: + raise ImportError( + "numpy is required for sine_wave_generator. Install it with: pip install numpy" + ) + + blocksize = sample_rate // 10 + total_frames = int((duration * sample_rate) // blocksize) + t_frame = np.arange(blocksize) / sample_rate + + for i in range(total_frames): + t = t_frame + i * blocksize / sample_rate + signal = amplitude * np.sin(2 * np.pi * freq * t) + signal_int16 = np.int16(signal * 32767) + frame = AudioFrame( + signal_int16.tobytes(), + sample_rate, + 1, + blocksize, + ) + yield frame diff --git a/livekit-rtc/livekit/rtc/version.py b/livekit-rtc/livekit/rtc/version.py index fee46bd8..69378e28 100644 --- a/livekit-rtc/livekit/rtc/version.py +++ b/livekit-rtc/livekit/rtc/version.py @@ -1 +1 @@ -__version__ = "0.11.1" +__version__ = "1.1.14" diff --git a/livekit-rtc/livekit/rtc/video_frame.py b/livekit-rtc/livekit/rtc/video_frame.py index 225243d1..30255323 100644 --- a/livekit-rtc/livekit/rtc/video_frame.py +++ b/livekit-rtc/livekit/rtc/video_frame.py @@ -18,10 +18,19 @@ from ._proto import ffi_pb2 as proto from typing import List, Optional from ._ffi_client import FfiClient, FfiHandle -from ._utils import get_address +from ._utils import _ensure_compatible_buffer, get_address + +from typing import Any class VideoFrame: + """ + Represents a video frame with associated metadata and pixel data. + + This class provides methods to access video frame properties such as width, height, + and pixel format, as well as methods for manipulating and converting video frames. + """ + def __init__( self, width: int, @@ -29,25 +38,61 @@ def __init__( type: proto_video.VideoBufferType.ValueType, data: Union[bytes, bytearray, memoryview], ) -> None: + """ + Initializes a new VideoFrame instance. + + Args: + width (int): The width of the video frame in pixels. + height (int): The height of the video frame in pixels. + type (proto_video.VideoBufferType.ValueType): The format type of the video frame data + (e.g., RGBA, BGRA, RGB24, etc.). + data (Union[bytes, bytearray, memoryview]): The raw pixel data for the video frame. + """ + data = _ensure_compatible_buffer(data) + self._width = width self._height = height self._type = type - self._data = bytearray(data) + self._data = data @property def width(self) -> int: + """ + Returns the width of the video frame in pixels. + + Returns: + int: The width of the video frame. + """ return self._width @property def height(self) -> int: + """ + Returns the height of the video frame in pixels. + + Returns: + int: The height of the video frame. + """ return self._height @property def type(self) -> proto_video.VideoBufferType.ValueType: + """ + Returns the height of the video frame in pixels. + + Returns: + int: The height of the video frame. + """ return self._type @property def data(self) -> memoryview: + """ + Returns a memoryview of the raw pixel data for the video frame. + + Returns: + memoryview: The raw pixel data of the video frame as a memoryview object. + """ return memoryview(self._data) @staticmethod @@ -62,19 +107,18 @@ def _from_owned_info(owned_info: proto_video.OwnedVideoBuffer) -> "VideoFrame": type=info.type, data=data, ) - FfiHandle(owned_info.handle.id) + FfiHandle(owned_info.handle.id).dispose() return frame def _proto_info(self) -> proto_video.VideoBufferInfo: info = proto_video.VideoBufferInfo() addr = get_address(self.data) - info.components.extend( - _get_plane_infos(addr, self.type, self.width, self.height) - ) + info.components.extend(_get_plane_infos(addr, self.type, self.width, self.height)) info.width = self.width info.height = self.height info.type = self.type info.data_ptr = addr + info.stride = 0 if self.type in [ proto_video.VideoBufferType.ARGB, @@ -89,9 +133,20 @@ def _proto_info(self) -> proto_video.VideoBufferInfo: return info def get_plane(self, plane_nth: int) -> Optional[memoryview]: - plane_infos = _get_plane_infos( - get_address(self.data), self.type, self.width, self.height - ) + """ + Returns the memoryview of a specific plane in the video frame, based on its index. + + Some video formats (e.g., I420, NV12) contain multiple planes (Y, U, V channels). + This method allows access to individual planes by index. + + Args: + plane_nth (int): The index of the plane to retrieve (starting from 0). + + Returns: + Optional[memoryview]: A memoryview of the specified plane's data, or None if + the index is out of bounds for the format. + """ + plane_infos = _get_plane_infos(get_address(self.data), self.type, self.width, self.height) if plane_nth >= len(plane_infos): return None @@ -102,6 +157,39 @@ def get_plane(self, plane_nth: int) -> Optional[memoryview]: def convert( self, type: proto_video.VideoBufferType.ValueType, *, flip_y: bool = False ) -> "VideoFrame": + """ + Converts the current video frame to a different format type, optionally flipping + the frame vertically. + + Args: + type (proto_video.VideoBufferType.ValueType): The target format type to convert to + (e.g., RGBA, I420). + flip_y (bool, optional): If True, the frame will be flipped vertically. Defaults to False. + + Returns: + VideoFrame: A new VideoFrame object in the specified format. + + Raises: + Exception: If the conversion isn't supported. + + Example: + Convert a frame from RGBA to I420 format: + + >>> frame = VideoFrame(width=1920, height=1080, type=proto_video.VideoBufferType.RGBA, data=raw_data) + >>> converted_frame = frame.convert(proto_video.VideoBufferType.I420) + >>> print(converted_frame.type) + VideoBufferType.I420 + + Example: + Convert a frame from BGRA to RGB24 format and flip it vertically: + + >>> frame = VideoFrame(width=1280, height=720, type=proto_video.VideoBufferType.BGRA, data=raw_data) + >>> converted_frame = frame.convert(proto_video.VideoBufferType.RGB24, flip_y=True) + >>> print(converted_frame.type) + VideoBufferType.RGB24 + >>> print(converted_frame.width, converted_frame.height) + 1280 720 + """ req = proto.FfiRequest() req.video_convert.flip_y = flip_y req.video_convert.dst_type = type @@ -112,6 +200,56 @@ def convert( return VideoFrame._from_owned_info(resp.video_convert.buffer) + def __repr__(self) -> str: + return f"rtc.VideoFrame(width={self.width}, height={self.height}, type={proto_video.VideoBufferType.Name(self.type)})" + + @classmethod + def __get_pydantic_core_schema__(cls, *_: Any) -> Any: + from pydantic_core import core_schema + import base64 + + def validate_video_frame(value: Any) -> "VideoFrame": + if isinstance(value, VideoFrame): + return value + + if isinstance(value, tuple): + value = value[0] + + if isinstance(value, dict): + return VideoFrame( + width=value["width"], + height=value["height"], + type=proto_video.VideoBufferType.Value(value["type"]), + data=base64.b64decode(value["data"]), + ) + + raise TypeError("Invalid type for VideoFrame") + + return core_schema.json_or_python_schema( + json_schema=core_schema.chain_schema( + [ + core_schema.model_fields_schema( + { + "width": core_schema.model_field(core_schema.int_schema()), + "height": core_schema.model_field(core_schema.int_schema()), + "type": core_schema.model_field(core_schema.str_schema()), + "data": core_schema.model_field(core_schema.str_schema()), + }, + ), + core_schema.no_info_plain_validator_function(validate_video_frame), + ] + ), + python_schema=core_schema.no_info_plain_validator_function(validate_video_frame), + serialization=core_schema.plain_serializer_function_ser_schema( + lambda instance: { + "width": instance.width, + "height": instance.height, + "type": proto_video.VideoBufferType.Name(instance.type), + "data": base64.b64encode(instance.data).decode("utf-8"), + } + ), + ) + def _component_info( data_ptr: int, stride: int, size: int @@ -123,11 +261,9 @@ def _component_info( return cmpt -def _get_plane_length( - type: proto_video.VideoBufferType.ValueType, width: int, height: int -) -> int: +def _get_plane_length(type: proto_video.VideoBufferType.ValueType, width: int, height: int) -> int: """ - Return the size in bytes of a participar video buffer type based on its size (This ignore the strides) + Return the size in bytes of a participant video buffer type based on its size (This ignores the strides) """ if type in [ proto_video.VideoBufferType.ARGB, @@ -169,32 +305,22 @@ def _get_plane_infos( chroma_width = (width + 1) // 2 chroma_height = (height + 1) // 2 y = _component_info(addr, width, width * height) - u = _component_info( - y.data_ptr + y.size, chroma_width, chroma_width * chroma_height - ) - v = _component_info( - u.data_ptr + u.size, chroma_width, chroma_width * chroma_height - ) + u = _component_info(y.data_ptr + y.size, chroma_width, chroma_width * chroma_height) + v = _component_info(u.data_ptr + u.size, chroma_width, chroma_width * chroma_height) return [y, u, v] elif type == proto_video.VideoBufferType.I420A: chroma_width = (width + 1) // 2 chroma_height = (height + 1) // 2 y = _component_info(addr, width, width * height) - u = _component_info( - y.data_ptr + y.size, chroma_width, chroma_width * chroma_height - ) - v = _component_info( - u.data_ptr + u.size, chroma_width, chroma_width * chroma_height - ) + u = _component_info(y.data_ptr + y.size, chroma_width, chroma_width * chroma_height) + v = _component_info(u.data_ptr + u.size, chroma_width, chroma_width * chroma_height) a = _component_info(v.data_ptr + v.size, width, width * height) return [y, u, v, a] elif type == proto_video.VideoBufferType.I422: chroma_width = (width + 1) // 2 y = _component_info(addr, width, width * height) u = _component_info(y.data_ptr + y.size, chroma_width, chroma_width * height) - v = _component_info( - u.data_ptr + u.size + u.size, chroma_width, chroma_width * height - ) + v = _component_info(u.data_ptr + u.size + u.size, chroma_width, chroma_width * height) return [y, u, v] elif type == proto_video.VideoBufferType.I444: y = _component_info(addr, width, width * height) @@ -205,12 +331,8 @@ def _get_plane_infos( chroma_width = (width + 1) // 2 chroma_height = (height + 1) // 2 y = _component_info(addr, width * 2, width * height * 2) - u = _component_info( - y.data_ptr + y.size, chroma_width * 2, chroma_width * chroma_height * 2 - ) - v = _component_info( - u.data_ptr + u.size, chroma_width * 2, chroma_width * chroma_height * 2 - ) + u = _component_info(y.data_ptr + y.size, chroma_width * 2, chroma_width * chroma_height * 2) + v = _component_info(u.data_ptr + u.size, chroma_width * 2, chroma_width * chroma_height * 2) return [y, u, v] elif type == proto_video.VideoBufferType.NV12: chroma_width = (width + 1) // 2 diff --git a/livekit-rtc/livekit/rtc/video_source.py b/livekit-rtc/livekit/rtc/video_source.py index 377f39f9..c8dd2a02 100644 --- a/livekit-rtc/livekit/rtc/video_source.py +++ b/livekit-rtc/livekit/rtc/video_source.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + from ._ffi_client import FfiHandle, FfiClient from ._proto import ffi_pb2 as proto_ffi from ._proto import video_frame_pb2 as proto_video @@ -19,11 +21,32 @@ class VideoSource: - def __init__(self, width: int, height: int) -> None: + def __init__(self, width: int, height: int, *, is_screencast: bool = False) -> None: + """ + Create a new video source. + + Args: + width (int): Initial width of the video source. + height (int): Initial height of the video source. + is_screencast (bool, optional): Optimize the WebRTC pipeline for screen content. + Defaults to False. + + When True, WebRTC will: + + - Maintain resolution under congestion by dropping frames instead of + downscaling (keeps text crisp) + - Disable quality scaling and denoising to preserve text/UI readability + - Guarantee a minimum 1200 kbps bitrate floor + - Enable zero-hertz mode, stopping frame transmission when the screen + is static to save bandwidth + - Set content type to screen, adjusting encoder configuration throughout + the pipeline (VP9 inter-layer prediction, simulcast layer allocation, etc.) + """ req = proto_ffi.FfiRequest() req.new_video_source.type = proto_video.VideoSourceType.VIDEO_SOURCE_NATIVE req.new_video_source.resolution.width = width req.new_video_source.resolution.height = height + req.new_video_source.is_screencast = is_screencast resp = FfiClient.instance.request(req) self._info = resp.new_video_source.source @@ -35,10 +58,17 @@ def capture_frame( *, timestamp_us: int = 0, rotation: proto_video.VideoRotation.ValueType = proto_video.VideoRotation.VIDEO_ROTATION_0, + metadata: proto_video.FrameMetadata | None = None, ) -> None: + """Capture a frame, optionally attaching frame metadata.""" req = proto_ffi.FfiRequest() req.capture_video_frame.source_handle = self._ffi_handle.handle req.capture_video_frame.buffer.CopyFrom(frame._proto_info()) req.capture_video_frame.rotation = rotation req.capture_video_frame.timestamp_us = timestamp_us + if metadata is not None: + req.capture_video_frame.metadata.CopyFrom(metadata) FfiClient.instance.request(req) + + async def aclose(self) -> None: + self._ffi_handle.dispose() diff --git a/livekit-rtc/livekit/rtc/video_stream.py b/livekit-rtc/livekit/rtc/video_stream.py index 5daf7e6b..eeced550 100644 --- a/livekit-rtc/livekit/rtc/video_stream.py +++ b/livekit-rtc/livekit/rtc/video_stream.py @@ -12,14 +12,18 @@ # See the License for the specific language governing permissions and # limitations under the License. +from __future__ import annotations + import asyncio from dataclasses import dataclass -from typing import Optional +from typing import Any, AsyncIterator, Optional -from ._ffi_client import FfiHandle, FfiClient +from ._ffi_client import FfiClient, FfiHandle from ._proto import ffi_pb2 as proto_ffi from ._proto import video_frame_pb2 as proto_video_frame +from ._proto.track_pb2 import TrackSource from ._utils import RingQueue, task_done_logger +from .participant import Participant from .track import Track from .video_frame import VideoFrame @@ -29,6 +33,7 @@ class VideoFrameEvent: frame: VideoFrame timestamp_us: int rotation: proto_video_frame.VideoRotation + metadata: proto_video_frame.FrameMetadata | None = None class VideoStream: @@ -37,34 +42,100 @@ class VideoStream: def __init__( self, track: Track, - *, loop: Optional[asyncio.AbstractEventLoop] = None, capacity: int = 0, format: Optional[proto_video_frame.VideoBufferType.ValueType] = None, + **kwargs: Any, ) -> None: - self._track = track self._loop = loop or asyncio.get_event_loop() - self._ffi_queue = FfiClient.instance.queue.subscribe(self._loop) - self._queue: RingQueue[VideoFrameEvent] = RingQueue(capacity) + # Only subscribe to video_stream_event to avoid unnecessary memory allocations + # from other event types (room_event, track_event, etc.) + self._ffi_queue = FfiClient.instance.queue.subscribe( + self._loop, + filter_fn=lambda e: e.WhichOneof("message") == "video_stream_event", + ) + self._queue: RingQueue[VideoFrameEvent | None] = RingQueue(capacity) + self._track: Track | None = track + self._format = format + self._capacity = capacity + self._format = format + stream: Any = None + if "participant" in kwargs: + stream = self._create_owned_stream_from_participant( + participant=kwargs["participant"], track_source=kwargs["track_source"] + ) + else: + stream = self._create_owned_stream() + + self._ffi_handle = FfiHandle(stream.handle.id) + self._info = stream.info + + self._task = self._loop.create_task(self._run()) + self._task.add_done_callback(task_done_logger) + @classmethod + def from_participant( + cls, + *, + participant: Participant, + track_source: TrackSource.ValueType, + loop: Optional[asyncio.AbstractEventLoop] = None, + format: Optional[proto_video_frame.VideoBufferType.ValueType] = None, + capacity: int = 0, + ) -> VideoStream: + return VideoStream( + participant=participant, + track_source=track_source, + loop=loop, + capacity=capacity, + format=format, + track=None, # type: ignore + ) + + @classmethod + def from_track( + cls, + *, + track: Track, + loop: Optional[asyncio.AbstractEventLoop] = None, + format: Optional[proto_video_frame.VideoBufferType.ValueType] = None, + capacity: int = 0, + ) -> VideoStream: + return VideoStream( + track=track, + loop=loop, + capacity=capacity, + format=format, + ) + + def __del__(self) -> None: + FfiClient.instance.queue.unsubscribe(self._ffi_queue) + + def _create_owned_stream(self) -> Any: + assert self._track is not None req = proto_ffi.FfiRequest() new_video_stream = req.new_video_stream - new_video_stream.track_handle = track._ffi_handle.handle + new_video_stream.track_handle = self._track._ffi_handle.handle new_video_stream.type = proto_video_frame.VideoStreamType.VIDEO_STREAM_NATIVE - if format is not None: - new_video_stream.format = format + if self._format is not None: + new_video_stream.format = self._format new_video_stream.normalize_stride = True - resp = FfiClient.instance.request(req) + return resp.new_video_stream.stream - stream_info = resp.new_video_stream.stream - self._ffi_handle = FfiHandle(stream_info.handle.id) - self._info = stream_info.info - self._task = self._loop.create_task(self._run()) - self._task.add_done_callback(task_done_logger) - - def __del__(self) -> None: - FfiClient.instance.queue.unsubscribe(self._ffi_queue) + def _create_owned_stream_from_participant( + self, participant: Participant, track_source: TrackSource.ValueType + ) -> Any: + req = proto_ffi.FfiRequest() + video_stream_from_participant = req.video_stream_from_participant + video_stream_from_participant.participant_handle = participant._ffi_handle.handle + video_stream_from_participant.type = proto_video_frame.VideoStreamType.VIDEO_STREAM_NATIVE + video_stream_from_participant.track_source = track_source + video_stream_from_participant.normalize_stride = True + if self._format is not None: + video_stream_from_participant.format = self._format + resp = FfiClient.instance.request(req) + return resp.video_stream_from_participant.stream async def _run(self) -> None: while True: @@ -72,13 +143,19 @@ async def _run(self) -> None: video_event = event.video_stream_event if video_event.HasField("frame_received"): - owned_buffer_info = video_event.frame_received.buffer + frame_received = video_event.frame_received + owned_buffer_info = frame_received.buffer frame = VideoFrame._from_owned_info(owned_buffer_info) + metadata: proto_video_frame.FrameMetadata | None = None + if frame_received.HasField("metadata"): + metadata = proto_video_frame.FrameMetadata() + metadata.CopyFrom(frame_received.metadata) event = VideoFrameEvent( frame=frame, - timestamp_us=video_event.frame_received.timestamp_us, - rotation=video_event.frame_received.rotation, + timestamp_us=frame_received.timestamp_us, + rotation=frame_received.rotation, + metadata=metadata, ) self._queue.put(event) @@ -91,13 +168,18 @@ async def aclose(self) -> None: self._ffi_handle.dispose() await self._task - def __aiter__(self) -> "VideoStream": - return self - def _is_event(self, e: proto_ffi.FfiEvent) -> bool: return e.video_stream_event.stream_handle == self._ffi_handle.handle + def __aiter__(self) -> AsyncIterator[VideoFrameEvent]: + return self + async def __anext__(self) -> VideoFrameEvent: if self._task.done(): raise StopAsyncIteration - return await self._queue.get() + + item = await self._queue.get() + if item is None: + raise StopAsyncIteration + + return item diff --git a/livekit-rtc/pyproject.toml b/livekit-rtc/pyproject.toml index e975c873..78299186 100644 --- a/livekit-rtc/pyproject.toml +++ b/livekit-rtc/pyproject.toml @@ -1,22 +1,76 @@ [build-system] requires = [ - "setuptools>=42", + "setuptools>=61", "wheel", "requests", ] build-backend = "setuptools.build_meta" +[project] +name = "livekit" +dynamic = ["version"] +description = "Python Real-time SDK for LiveKit" +readme = "README.md" +requires-python = ">=3.9.0" +license = "Apache-2.0" +keywords = ["webrtc", "realtime", "audio", "video", "livekit"] +authors = [ + { name = "LiveKit", email = "support@livekit.io" } +] +classifiers = [ + "Intended Audience :: Developers", + "Topic :: Multimedia :: Sound/Audio", + "Topic :: Multimedia :: Video", + "Topic :: Scientific/Engineering :: Artificial Intelligence", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", + "Programming Language :: Python :: 3 :: Only", +] +dependencies = [ + "protobuf>=5", + "types-protobuf>=5", + "aiofiles>=24", + "numpy>=1.26", +] + +[project.urls] +Documentation = "https://docs.livekit.io" +Website = "https://livekit.io/" +Source = "https://github.com/livekit/python-sdks/" + +[tool.setuptools.dynamic] +version = { attr = "livekit.rtc.version.__version__" } + +[tool.setuptools.packages.find] +include = ["livekit.*"] + +[tool.setuptools.package-data] +"livekit.rtc" = ["_proto/*.py", "py.typed", "*.pyi", "**/*.pyi"] +"livekit.rtc.resources" = [ + "*.so", + "*.dylib", + "*.dll", + "LICENSE.md", + "*.h", + "jupyter-html/index.html", +] + [tool.cibuildwheel] build = "cp39-*" -skip = "*-musllinux_*" # not supported (libwebrtc is using glibc) - +skip = "*-musllinux_*" # not supported (libwebrtc requires glibc) before-build = "pip install requests && python rust-sdks/download_ffi.py --output livekit/rtc/resources" -manylinux-x86_64-image = "manylinux_2_28" -manylinux-i686-image = "manylinux_2_28" -manylinux-aarch64-image = "manylinux_2_28" -manylinux-ppc64le-image = "manylinux_2_28" -manylinux-s390x-image = "manylinux_2_28" -manylinux-pypy_x86_64-image = "manylinux_2_28" -manylinux-pypy_i686-image = "manylinux_2_28" -manylinux-pypy_aarch64-image = "manylinux_2_28" +# macOS deployment targets must match the FFI binaries (see rust-sdks/.github/workflows/ffi-builds.yml) +# x86_64 supports macOS 10.15+, arm64 requires macOS 11.0+ +[[tool.cibuildwheel.overrides]] +select = "*macosx_x86_64" +environment = { MACOSX_DEPLOYMENT_TARGET = "10.15" } + +[[tool.cibuildwheel.overrides]] +select = "*macosx_arm64" +environment = { MACOSX_DEPLOYMENT_TARGET = "11.0" } diff --git a/livekit-rtc/rust-sdks b/livekit-rtc/rust-sdks index ea0bc400..63128d01 160000 --- a/livekit-rtc/rust-sdks +++ b/livekit-rtc/rust-sdks @@ -1 +1 @@ -Subproject commit ea0bc40093ebcc7d308b2c4e8d088340557bdf46 +Subproject commit 63128d01d955d9d8967544f46cff64a361232bf6 diff --git a/livekit-rtc/setup.py b/livekit-rtc/setup.py index 8c5e8d44..279e8a19 100644 --- a/livekit-rtc/setup.py +++ b/livekit-rtc/setup.py @@ -12,60 +12,66 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Custom setup.py for platform-specific wheel tagging. + +This file exists solely to customize the wheel platform tag. All package metadata +is defined in pyproject.toml. + +The native FFI libraries (.so/.dylib/.dll) require specific platform tags that +respect MACOSX_DEPLOYMENT_TARGET and ARCHFLAGS environment variables set by +cibuildwheel, rather than using sysconfig.get_platform() which returns Python's +compile-time values. +""" + import os -import pathlib -from typing import Any, Dict +import platform +import sys import setuptools # type: ignore -import setuptools.command.build_py # type: ignore from wheel.bdist_wheel import bdist_wheel as _bdist_wheel # type: ignore -from wheel.bdist_wheel import get_platform # type: ignore -here = pathlib.Path(__file__).parent.resolve() -about: Dict[Any, Any] = {} -with open(os.path.join(here, "livekit", "rtc", "version.py"), "r") as f: - exec(f.read(), about) + +def get_platform_tag(): + """Get the wheel platform tag for the current/target platform.""" + if sys.platform == "darwin": + # Get deployment target from environment (set by cibuildwheel) or fall back + target = os.environ.get("MACOSX_DEPLOYMENT_TARGET") + if not target: + target = platform.mac_ver()[0] + parts = target.split(".") + target = f"{parts[0]}.{parts[1] if len(parts) > 1 else '0'}" + + version_tag = target.replace(".", "_") + + # Check ARCHFLAGS for cross-compilation (cibuildwheel sets this) + archflags = os.environ.get("ARCHFLAGS", "") + if "-arch arm64" in archflags: + arch = "arm64" + elif "-arch x86_64" in archflags: + arch = "x86_64" + else: + arch = platform.machine() + + return f"macosx_{version_tag}_{arch}" + elif sys.platform == "linux": + return f"linux_{platform.machine()}" + elif sys.platform == "win32": + arch = platform.machine() + if arch == "AMD64": + arch = "amd64" + return f"win_{arch}" + else: + return f"{platform.system().lower()}_{platform.machine()}" class bdist_wheel(_bdist_wheel): def finalize_options(self): - self.plat_name = get_platform(None) # force a platform tag + self.plat_name = get_platform_tag() _bdist_wheel.finalize_options(self) setuptools.setup( - name="livekit", - version=about["__version__"], - description="Python Real-time SDK for LiveKit", - long_description=(here / "README.md").read_text(encoding="utf-8"), - long_description_content_type="text/markdown", - url="https://github.com/livekit/python-sdks", cmdclass={ "bdist_wheel": bdist_wheel, }, - classifiers=[ - "Intended Audience :: Developers", - "License :: OSI Approved :: Apache Software License", - "Topic :: Multimedia :: Sound/Audio", - "Topic :: Multimedia :: Video", - "Topic :: Scientific/Engineering :: Artificial Intelligence", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3 :: Only", - ], - keywords=["webrtc", "realtime", "audio", "video", "livekit"], - license="Apache-2.0", - packages=setuptools.find_namespace_packages(include=["livekit.*"]), - python_requires=">=3.9.0", - install_requires=["protobuf>=3", "types-protobuf>=3"], - package_data={ - "livekit.rtc": ["_proto/*.py", "py.typed", "*.pyi", "**/*.pyi"], - "livekit.rtc.resources": ["*.so", "*.dylib", "*.dll", "LICENSE.md", "*.h"], - }, - project_urls={ - "Documentation": "https://docs.livekit.io", - "Website": "https://livekit.io/", - "Source": "https://github.com/livekit/python-sdks/", - }, ) diff --git a/livekit-rtc/tests/conftest.py b/livekit-rtc/tests/conftest.py new file mode 100644 index 00000000..03812308 --- /dev/null +++ b/livekit-rtc/tests/conftest.py @@ -0,0 +1,27 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Put this tests directory on sys.path so test modules can `from utils import ...`. + +Both `livekit-rtc/tests/` and the repo-root `tests/` are collected by pytest +together; if either has an `__init__.py` they collide on the `tests` namespace +package name. So we keep this directory non-package and use absolute imports. +""" + +import sys +from pathlib import Path + +_TESTS_DIR = Path(__file__).resolve().parent +if str(_TESTS_DIR) not in sys.path: + sys.path.insert(0, str(_TESTS_DIR)) diff --git a/livekit-rtc/tests/test_audio.py b/livekit-rtc/tests/test_audio.py new file mode 100644 index 00000000..4ae3ef6b --- /dev/null +++ b/livekit-rtc/tests/test_audio.py @@ -0,0 +1,247 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""End-to-end audio publish/subscribe tests.""" + +from __future__ import annotations + +import asyncio +import ctypes +import math +import os +import wave +from pathlib import Path + +import numpy as np +import pytest + +from livekit import rtc +from livekit.rtc.audio_frame import AudioFrame + +from utils import create_token, skip_if_no_credentials, unique_room_name # type: ignore[import-not-found] + + +SAMPLE_RATE = 48000 +NUM_CHANNELS = 1 +TONE_DURATION_SEC = 1.0 +FREQUENCIES_HZ = [100, 300, 500, 700, 1000] +AMPLITUDE = 0.5 + + +def _generate_sine_wave( + frequency: int, + sample_rate: int, + num_channels: int, + duration_sec: float, + amplitude: float = 0.5, +) -> AudioFrame: + """Generate an AudioFrame containing a sine wave at the given frequency.""" + samples_per_channel = int(sample_rate * duration_sec) + t = np.arange(samples_per_channel, dtype=np.float64) / sample_rate + wave_signal = np.sin(2.0 * math.pi * frequency * t) * amplitude + pcm = (wave_signal * np.iinfo(np.int16).max).astype(np.int16) + + if num_channels > 1: + pcm = np.repeat(pcm[:, np.newaxis], num_channels, axis=1).reshape(-1) + + return AudioFrame( + data=pcm.tobytes(), + sample_rate=sample_rate, + num_channels=num_channels, + samples_per_channel=samples_per_channel, + ) + + +def _frame_to_mono_float(frame: AudioFrame) -> np.ndarray: + """Decode an int16 AudioFrame into a normalized float64 mono signal.""" + samples = np.frombuffer(bytes(frame.data.cast("B")), dtype=np.int16).astype(np.float64) + if frame.num_channels > 1: + samples = samples.reshape(-1, frame.num_channels).mean(axis=1) + return samples / float(np.iinfo(np.int16).max) + + +def _fft_spectrum(frame: AudioFrame) -> tuple[np.ndarray, np.ndarray]: + """Return (freqs, magnitudes) from a Hann-windowed rfft of `frame`.""" + signal = _frame_to_mono_float(frame) + window = np.hanning(len(signal)) + # Compensate for the Hann window's coherent gain so magnitudes stay comparable. + spectrum = np.fft.rfft(signal * window) / (np.sum(window) / 2.0) + magnitudes = np.abs(spectrum) + freqs = np.fft.rfftfreq(len(signal), d=1.0 / frame.sample_rate) + return freqs, magnitudes + + +def _detect_peak_frequency(frame: AudioFrame) -> float: + """Return the frequency bin with the largest magnitude in `frame`.""" + freqs, magnitudes = _fft_spectrum(frame) + return float(freqs[int(np.argmax(magnitudes))]) + + +def _band_energies( + frame: AudioFrame, + centers: list[int], + bandwidth_hz: float = 20.0, +) -> dict[int, float]: + """Sum squared-magnitude (energy) in narrow bands centered at each frequency.""" + freqs, magnitudes = _fft_spectrum(frame) + power = magnitudes**2 + return { + center: float( + np.sum(power[(freqs >= center - bandwidth_hz) & (freqs <= center + bandwidth_hz)]) + ) + for center in centers + } + + +@skip_if_no_credentials() +class TestAudioStreamPublishSubscribe: + """End-to-end: publish a sine sweep into a room and verify spectrum on the subscriber.""" + + async def test_audio_stream_publish_subscribe(self) -> None: + """Publish 5 seconds of 100/300/500/700/1000 Hz tones and FFT-verify received audio.""" + url = os.environ["LIVEKIT_URL"] + room_name = unique_room_name("test-audio-sweep") + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token("audio-sweep-publisher", room_name) + subscriber_token = create_token("audio-sweep-subscriber", room_name) + + track_subscribed_event = asyncio.Event() + subscribed_track: rtc.Track | None = None + + @subscriber_room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal subscribed_track + if track.kind == rtc.TrackKind.KIND_AUDIO: + subscribed_track = track + track_subscribed_event.set() + + try: + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + source = rtc.AudioSource(SAMPLE_RATE, NUM_CHANNELS) + track = rtc.LocalAudioTrack.create_audio_track("sine-sweep", source) + options = rtc.TrackPublishOptions() + options.source = rtc.TrackSource.SOURCE_MICROPHONE + await publisher_room.local_participant.publish_track(track, options) + + await asyncio.wait_for(track_subscribed_event.wait(), timeout=10.0) + assert subscribed_track is not None + + audio_stream = rtc.AudioStream( + subscribed_track, + sample_rate=SAMPLE_RATE, + num_channels=NUM_CHANNELS, + ) + + total_duration = TONE_DURATION_SEC * len(FREQUENCIES_HZ) + target_samples = int(SAMPLE_RATE * total_duration) + # Collect a little extra to tolerate codec startup latency. + collect_samples_target = target_samples + int(SAMPLE_RATE * 1.0) + + async def publish_tones() -> None: + await track_subscribed_event.wait() + for freq in FREQUENCIES_HZ: + frame = _generate_sine_wave( + freq, + SAMPLE_RATE, + NUM_CHANNELS, + TONE_DURATION_SEC, + AMPLITUDE, + ) + await source.capture_frame(frame) + await source.wait_for_playout() + + async def collect_samples() -> np.ndarray: + buffers: list[np.ndarray] = [] + total = 0 + async for event in audio_stream: + chunk = np.frombuffer(bytes(event.frame.data.cast("B")), dtype=np.int16) + buffers.append(chunk) + total += len(chunk) + if total >= collect_samples_target: + break + return np.concatenate(buffers) if buffers else np.array([], dtype=np.int16) + + publish_task = asyncio.create_task(publish_tones()) + received = await asyncio.wait_for(collect_samples(), timeout=20.0) + await publish_task + await audio_stream.aclose() + await source.aclose() + + assert len(received) >= target_samples, ( + f"Expected at least {target_samples} samples, got {len(received)}" + ) + + recv_wav_path = Path(__file__).parent / "subscriber_recv_freqs.wav" + with wave.open(str(recv_wav_path), "wb") as wav_out: + wav_out.setnchannels(NUM_CHANNELS) + wav_out.setsampwidth(ctypes.sizeof(ctypes.c_int16)) + wav_out.setframerate(SAMPLE_RATE) + wav_out.writeframes(received.tobytes()) + + # Find signal onset to skip codec startup silence. + envelope = np.abs(received.astype(np.float32)) + threshold = float(envelope.max()) * 0.2 + onset_candidates = np.where(envelope > threshold)[0] + assert onset_candidates.size > 0, "Received audio contains only silence" + onset = int(onset_candidates[0]) + + samples_per_tone = int(SAMPLE_RATE * TONE_DURATION_SEC) + # Analyze the middle slice of each tone window to avoid boundary transitions. + analysis_margin = int(SAMPLE_RATE * 0.2) + analysis_length = samples_per_tone - 2 * analysis_margin + + per_tone_peaks: list[tuple[int, float]] = [] + for idx, expected_freq in enumerate(FREQUENCIES_HZ): + start = onset + idx * samples_per_tone + analysis_margin + end = start + analysis_length + assert end <= len(received), ( + f"Not enough samples for tone {idx} (expected {expected_freq} Hz): " + f"need {end}, have {len(received)}" + ) + segment = received[start:end] + segment_frame = AudioFrame( + data=segment.tobytes(), + sample_rate=SAMPLE_RATE, + num_channels=NUM_CHANNELS, + samples_per_channel=len(segment), + ) + peak_hz = _detect_peak_frequency(segment_frame) + per_tone_peaks.append((expected_freq, peak_hz)) + + # Opus transcoding adds spectral jitter; allow a 15 Hz tolerance. + assert peak_hz == pytest.approx(expected_freq, abs=15.0), ( + f"Tone {idx}: expected {expected_freq} Hz, got peak at {peak_hz:.1f} Hz. " + f"All peaks: {per_tone_peaks}" + ) + + # The target band should also dominate the other sweep bands. + energies = _band_energies(segment_frame, FREQUENCIES_HZ, bandwidth_hz=30.0) + target_energy = energies[expected_freq] + other_energy = sum(v for k, v in energies.items() if k != expected_freq) + assert target_energy > 5.0 * max(other_energy, 1e-12), ( + f"Tone {idx} ({expected_freq} Hz) did not dominate other bands: " + f"target={target_energy:.3e}, other={other_energy:.3e}" + ) + finally: + await publisher_room.disconnect() + await subscriber_room.disconnect() diff --git a/livekit-rtc/tests/test_audio_stream_init.py b/livekit-rtc/tests/test_audio_stream_init.py new file mode 100644 index 00000000..80da28ab --- /dev/null +++ b/livekit-rtc/tests/test_audio_stream_init.py @@ -0,0 +1,53 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Regression tests for AudioStream.__init__ error handling.""" + +from __future__ import annotations + +import asyncio +from unittest.mock import MagicMock, patch + +import pytest + +from livekit import rtc + + +async def test_failed_stream_creation_does_not_orphan_run_task() -> None: + """If owned-stream creation fails, __init__ must not leave a running _run task. + + _run dereferences self._ffi_handle, which is only assigned once stream creation + succeeds. Scheduling the task before that assignment leaves an orphaned task that + raises AttributeError from the event loop, uncatchable by the caller. + """ + track = MagicMock(spec=rtc.Track) + before = asyncio.all_tasks() + + with patch.object( + rtc.AudioStream, + "_create_owned_stream", + side_effect=RuntimeError("track already closed"), + ): + with pytest.raises(RuntimeError, match="track already closed"): + rtc.AudioStream(track) + + orphaned = [ + t + for t in asyncio.all_tasks() - before + if getattr(t.get_coro(), "__qualname__", "") == "AudioStream._run" + ] + for t in orphaned: + t.cancel() + + assert not orphaned diff --git a/livekit-rtc/tests/test_audio_stream_room_lifecycle.py b/livekit-rtc/tests/test_audio_stream_room_lifecycle.py new file mode 100644 index 00000000..99f316cd --- /dev/null +++ b/livekit-rtc/tests/test_audio_stream_room_lifecycle.py @@ -0,0 +1,736 @@ +"""Unit tests for Track-driven FrameProcessor metadata propagation. + +These tests drive the **real** `rtc.Track`, `rtc.Room`, `rtc.RemoteParticipant`, +and `rtc.RemoteTrackPublication` objects — constructed via `__new__` injection +to bypass FFI — against a `_RecordingProcessor` test double. They cover the +contract that an attached FrameProcessor receives: + - `_on_stream_info_updated` / `_on_credentials_updated` on every room transition + or token refresh, + - `_on_stream_info_cleared` / `_on_credentials_cleared` when the track leaves a room, +and that `AudioStream.aclose()` honors `auto_close_noise_cancellation`. +""" + +from __future__ import annotations + +import asyncio +import weakref +from types import SimpleNamespace +from typing import Any, Optional, cast + +import pytest + +from livekit import rtc +from livekit.rtc._ffi_client import FfiClient +from livekit.rtc._proto import ffi_pb2 as proto_ffi +from livekit.rtc._proto import participant_pb2 as proto_participant +from livekit.rtc._proto import room_pb2 as proto_room +from livekit.rtc._proto import track_pb2 as proto_track +from livekit.rtc._utils import BroadcastQueue +from livekit.rtc.event_emitter import EventEmitter + + +# -- real-object helpers ------------------------------------------------------ + + +def _make_room(name: str = "room-x", token: str = "tok-x", url: str = "wss://x") -> rtc.Room: + """Build a real `rtc.Room` via __new__, injecting just the fields Track reads.""" + room = rtc.Room.__new__(rtc.Room) + EventEmitter.__init__(room) + room._info = proto_room.RoomInfo(name=name) + room._token = token + room._server_url = url + room._remote_participants = {} + room._local_participant = None + room._ffi_handle = None # `Room.__del__` reads this on GC + return room + + +def _make_remote_participant(identity: str) -> rtc.RemoteParticipant: + p = rtc.RemoteParticipant.__new__(rtc.RemoteParticipant) + p._info = proto_participant.ParticipantInfo(identity=identity) + p._track_publications = {} + return p + + +def _make_remote_publication(sid: str) -> rtc.RemoteTrackPublication: + pub = rtc.RemoteTrackPublication.__new__(rtc.RemoteTrackPublication) + pub._info = proto_track.TrackPublicationInfo(sid=sid) + return pub + + +def _make_local_participant(identity: str) -> rtc.LocalParticipant: + p = rtc.LocalParticipant.__new__(rtc.LocalParticipant) + p._info = proto_participant.ParticipantInfo(identity=identity) + p._track_publications = {} + return p + + +def _make_local_publication(sid: str) -> rtc.LocalTrackPublication: + pub = rtc.LocalTrackPublication.__new__(rtc.LocalTrackPublication) + pub._info = proto_track.TrackPublicationInfo(sid=sid) + pub._track = None + return pub + + +def _make_track(sid: str = "TR_x") -> rtc.Track: + track = rtc.Track.__new__(rtc.Track) + track._info = proto_track.TrackInfo(sid=sid) + track._ffi_handle = cast(Any, None) + track._room_ref = None + track._audio_streams = weakref.WeakSet() + return track + + +def _attach_publication(room: rtc.Room, *, identity: str, track_sid: str, pub_sid: str) -> None: + participant = _make_remote_participant(identity=identity) + participant._track_publications[track_sid] = _make_remote_publication(sid=pub_sid) + room._remote_participants[identity] = participant + + +def _make_stream( + *, + track: Optional[rtc.Track] = None, + processor: Optional[rtc.FrameProcessor[rtc.AudioFrame]] = None, +) -> rtc.AudioStream: + """Build an AudioStream without going through the FFI-touching __init__.""" + stream = rtc.AudioStream.__new__(rtc.AudioStream) + stream._track = track + stream._processor = processor + stream._audio_filter_module = None + stream._audio_filter_options = None + + if track is not None: + track._register_audio_stream(stream) + return stream + + +def _make_closeable_stream( + *, + track: Optional[rtc.Track] = None, + processor: Optional[rtc.FrameProcessor[rtc.AudioFrame]] = None, + auto_close: bool = True, +) -> rtc.AudioStream: + """Extends _make_stream with the minimal state `aclose()` touches.""" + stream = _make_stream(track=track, processor=processor) + stream._processor_auto_close = auto_close + stream._task = asyncio.ensure_future(asyncio.sleep(0)) + stream._ffi_handle = cast(Any, SimpleNamespace(dispose=lambda: None)) + return stream + + +class _RecordingProcessor(rtc.FrameProcessor[rtc.AudioFrame]): + def __init__(self) -> None: + self._enabled = True + self.stream_info_calls: list[dict[str, str]] = [] + self.credentials_calls: list[dict[str, str]] = [] + self.stream_info_cleared_calls: int = 0 + self.credentials_cleared_calls: int = 0 + self.close_calls = 0 + + @property + def enabled(self) -> bool: + return self._enabled + + @enabled.setter + def enabled(self, value: bool) -> None: + self._enabled = value + + def _on_stream_info_updated( + self, *, room_name: str, participant_identity: str, publication_sid: str + ) -> None: + self.stream_info_calls.append( + { + "room_name": room_name, + "participant_identity": participant_identity, + "publication_sid": publication_sid, + } + ) + + def _on_stream_info_cleared(self) -> None: + self.stream_info_cleared_calls += 1 + + def _on_credentials_updated(self, *, token: str, url: str) -> None: + self.credentials_calls.append({"token": token, "url": url}) + + def _on_credentials_cleared(self) -> None: + self.credentials_cleared_calls += 1 + + def _process(self, frame: rtc.AudioFrame) -> rtc.AudioFrame: + return frame + + def _close(self) -> None: + self.close_calls += 1 + + +def test_processor_receives_lifecycle_callbacks_on_room_attach() -> None: + room = _make_room(name="room-1", token="tok-1", url="wss://room-1") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + assert len(processor.stream_info_calls) == 1 + assert processor.stream_info_calls[0] == { + "room_name": "room-1", + "participant_identity": "alice", + "publication_sid": "PUB_1", + } + assert len(processor.credentials_calls) == 1 + assert processor.credentials_calls[0] == {"token": "tok-1", "url": "wss://room-1"} + + +def test_processor_callbacks_refire_on_track_room_change() -> None: + room_a = _make_room(name="room-a", token="tok-a", url="wss://a") + _attach_publication(room_a, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room_a) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + assert len(processor.stream_info_calls) == 1 + assert len(processor.credentials_calls) == 1 + + room_b = _make_room(name="room-b", token="tok-b", url="wss://b") + _attach_publication(room_b, identity="bob", track_sid="TR_1", pub_sid="PUB_2") + track._set_room(room_b) + + assert len(processor.stream_info_calls) == 2 + assert processor.stream_info_calls[-1] == { + "room_name": "room-b", + "participant_identity": "bob", + "publication_sid": "PUB_2", + } + assert len(processor.credentials_calls) == 2 + assert processor.credentials_calls[-1] == {"token": "tok-b", "url": "wss://b"} + + +def test_token_refresh_propagates_to_processor() -> None: + room = _make_room(name="room-1", token="tok-initial", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + baseline_creds = len(processor.credentials_calls) + baseline_info = len(processor.stream_info_calls) + + room._token = "tok-rotated" + room.emit("token_refreshed") + + assert len(processor.credentials_calls) == baseline_creds + 1 + assert processor.credentials_calls[-1] == {"token": "tok-rotated", "url": "wss://r"} + assert len(processor.stream_info_calls) == baseline_info + + +def test_repeated_set_room_with_same_room_does_not_double_register_listener() -> None: + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + track._set_room(room) # idempotent set with the same room + + baseline = len(processor.credentials_calls) + room._token = "tok-rotated" + room.emit("token_refreshed") + + assert len(processor.credentials_calls) == baseline + 1 + + +def test_set_room_swaps_listener_to_new_room() -> None: + room_a = _make_room(name="a", token="ta", url="wss://a") + _attach_publication(room_a, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + room_b = _make_room(name="b", token="tb", url="wss://b") + _attach_publication(room_b, identity="bob", track_sid="TR_1", pub_sid="PUB_2") + + track = _make_track(sid="TR_1") + track._set_room(room_a) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + track._set_room(room_b) + + creds_before_a_emit = len(processor.credentials_calls) + room_a._token = "ta-rotated" + room_a.emit("token_refreshed") + assert len(processor.credentials_calls) == creds_before_a_emit, ( + "token_refreshed on the old room must not reach the processor" + ) + + room_b._token = "tb-rotated" + room_b.emit("token_refreshed") + assert len(processor.credentials_calls) == creds_before_a_emit + 1 + assert processor.credentials_calls[-1] == {"token": "tb-rotated", "url": "wss://b"} + + +def test_unregister_audio_stream_stops_metadata_pushes() -> None: + """After a stream is unregistered from the track, subsequent room events + (e.g. token_refreshed) must not reach the stream's processor.""" + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + stream = _make_stream(track=track, processor=processor) + + track._unregister_audio_stream(stream) + + baseline = len(processor.credentials_calls) + room._token = "tok-rotated" + room.emit("token_refreshed") + + assert len(processor.credentials_calls) == baseline, ( + "unregistered stream's processor must not receive credentials on token_refreshed" + ) + + +def test_track_leaving_room_clears_processor_metadata() -> None: + """When a track's room transitions to None (e.g. on unsubscribe/unpublish), + the processor receives `_on_*_cleared` calls.""" + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + assert len(processor.stream_info_calls) == 1 + assert len(processor.credentials_calls) == 1 + assert processor.stream_info_cleared_calls == 0 + assert processor.credentials_cleared_calls == 0 + + track._set_room(None) + + # _updated lists unchanged; _cleared counters bumped exactly once each + assert len(processor.stream_info_calls) == 1 + assert len(processor.credentials_calls) == 1 + assert processor.stream_info_cleared_calls == 1 + assert processor.credentials_cleared_calls == 1 + + +def test_fanout_to_multiple_registered_streams() -> None: + """A Track with multiple registered AudioStreams fans metadata out to all + of them on room attach AND on token refresh.""" + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + + processor1 = _RecordingProcessor() + processor2 = _RecordingProcessor() + _s1 = _make_stream(track=track, processor=processor1) # noqa: F841 + _s2 = _make_stream(track=track, processor=processor2) # noqa: F841 + + # both processors got the initial push on registration + assert len(processor1.stream_info_calls) == 1 + assert len(processor1.credentials_calls) == 1 + assert len(processor2.stream_info_calls) == 1 + assert len(processor2.credentials_calls) == 1 + + room._token = "tok-rotated" + room.emit("token_refreshed") + + # token refresh fans credentials to BOTH processors; no new stream_info + assert len(processor1.credentials_calls) == 2 + assert processor1.credentials_calls[-1] == {"token": "tok-rotated", "url": "wss://r"} + assert len(processor2.credentials_calls) == 2 + assert processor2.credentials_calls[-1] == {"token": "tok-rotated", "url": "wss://r"} + assert len(processor1.stream_info_calls) == 1 + assert len(processor2.stream_info_calls) == 1 + + +def test_register_audio_stream_before_track_enters_room() -> None: + """A stream registered against a Track that has no room yet receives no + metadata until the Track is moved into a room.""" + track = _make_track(sid="TR_1") # track has no room + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + # nothing pushed yet — Track is roomless + assert len(processor.stream_info_calls) == 0 + assert len(processor.credentials_calls) == 0 + + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track._set_room(room) + + # now the stream's processor sees the metadata + assert len(processor.stream_info_calls) == 1 + assert processor.stream_info_calls[0] == { + "room_name": "room-1", + "participant_identity": "alice", + "publication_sid": "PUB_1", + } + assert len(processor.credentials_calls) == 1 + assert processor.credentials_calls[0] == {"token": "tok-1", "url": "wss://r"} + + +def test_track_room_cycle_attach_detach_reattach() -> None: + """Track enters room A → leaves to None → enters room B. Processor sees + real(A), cleared, real(B); listener fully migrates each time.""" + room_a = _make_room(name="a", token="ta", url="wss://a") + _attach_publication(room_a, identity="alice", track_sid="TR_1", pub_sid="PUB_A") + room_b = _make_room(name="b", token="tb", url="wss://b") + _attach_publication(room_b, identity="bob", track_sid="TR_1", pub_sid="PUB_B") + + track = _make_track(sid="TR_1") + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + track._set_room(room_a) + track._set_room(None) + track._set_room(room_b) + + # two real updates (A then B) interleaved with one clear pass + assert len(processor.stream_info_calls) == 2 + assert processor.stream_info_calls[0]["room_name"] == "a" + assert processor.stream_info_calls[0]["publication_sid"] == "PUB_A" + assert processor.stream_info_calls[1]["room_name"] == "b" + assert processor.stream_info_calls[1]["publication_sid"] == "PUB_B" + + assert len(processor.credentials_calls) == 2 + assert processor.credentials_calls[0] == {"token": "ta", "url": "wss://a"} + assert processor.credentials_calls[1] == {"token": "tb", "url": "wss://b"} + + assert processor.stream_info_cleared_calls == 1 + assert processor.credentials_cleared_calls == 1 + + # listener fully detached from room_a; only room_b reaches the processor now + creds_after_cycle = len(processor.credentials_calls) + room_a._token = "ta-rotated" + room_a.emit("token_refreshed") + assert len(processor.credentials_calls) == creds_after_cycle, ( + "token_refreshed on the abandoned room must not reach the processor" + ) + + room_b._token = "tb-rotated" + room_b.emit("token_refreshed") + assert len(processor.credentials_calls) == creds_after_cycle + 1 + assert processor.credentials_calls[-1] == {"token": "tb-rotated", "url": "wss://b"} + + +def test_set_room_with_no_registered_streams_is_safe() -> None: + """Setting a room (or clearing it) on a Track with no registered streams + must be a safe no-op. Subsequent stream registration still picks up the + current room's metadata.""" + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + + track = _make_track(sid="TR_1") + track._set_room(room) # no streams yet — must not raise + track._set_room(None) # detach — must not raise + track._set_room(room) # re-attach — still safe + + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + assert len(processor.stream_info_calls) == 1 + assert processor.stream_info_calls[0]["room_name"] == "room-1" + assert len(processor.credentials_calls) == 1 + + +def test_unregister_one_of_many_streams_only_fans_out_to_remaining() -> None: + """When one of many streams is unregistered, subsequent token_refresh + reaches only the remaining streams' processors.""" + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + + processor1 = _RecordingProcessor() + processor2 = _RecordingProcessor() + stream1 = _make_stream(track=track, processor=processor1) + _stream2 = _make_stream(track=track, processor=processor2) # noqa: F841 + + track._unregister_audio_stream(stream1) + + p1_creds_before = len(processor1.credentials_calls) + p2_creds_before = len(processor2.credentials_calls) + + room._token = "tok-rotated" + room.emit("token_refreshed") + + assert len(processor1.credentials_calls) == p1_creds_before, ( + "unregistered stream's processor must not receive the refreshed credentials" + ) + assert len(processor2.credentials_calls) == p2_creds_before + 1 + assert processor2.credentials_calls[-1] == {"token": "tok-rotated", "url": "wss://r"} + + +@pytest.mark.asyncio +async def test_aclose_closes_processor_when_auto_close_true() -> None: + """`aclose()` calls `_close()` on the attached FrameProcessor when + `auto_close_noise_cancellation` is True (the default).""" + processor = _RecordingProcessor() + stream = _make_closeable_stream(processor=processor, auto_close=True) + + await stream.aclose() + + assert processor.close_calls == 1 + + +@pytest.mark.asyncio +async def test_aclose_leaves_processor_open_when_auto_close_false() -> None: + """`aclose()` does NOT call `_close()` when `auto_close_noise_cancellation` + is False — the agents SDK path for sharing one processor across many track + attach/detach cycles.""" + processor = _RecordingProcessor() + stream = _make_closeable_stream(processor=processor, auto_close=False) + + await stream.aclose() + + assert processor.close_calls == 0 + + +# -- local_track_republished regression --------------------------------------- + + +def test_local_track_republished_updates_track_sid_and_repushes_metadata() -> None: + """A full-reconnect republish re-issues the publication SID. The handler must + keep the local-track invariant (track.sid == publication.sid) intact and + re-push metadata so attached processors learn the new SID — otherwise the + SID-based lookup in Track._push_processor_metadata_to_stream fails and the + processor receives empty participant_identity / publication_sid. + """ + room = _make_room(name="room-1", token="tok-1", url="wss://r") + local = _make_local_participant("agent") + room._local_participant = local + + # Local track published under the OLD sid (mirrors publish_track: + # track.sid == publication.sid). + track = _make_track(sid="OLD") + publication = _make_local_publication(sid="OLD") + publication._track = track + local._track_publications["OLD"] = publication + track._set_room(room) + + # Processor attached before the reconnect sees the OLD sid. + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + assert processor.stream_info_calls[-1] == { + "room_name": "room-1", + "participant_identity": "agent", + "publication_sid": "OLD", + } + + # Dispatch a synthetic local_track_republished re-issuing the sid as NEW. + event = proto_room.RoomEvent( + local_track_republished=proto_room.LocalTrackRepublished( + previous_sid="OLD", + info=proto_track.TrackPublicationInfo(sid="NEW"), + ) + ) + room._on_room_event(event) + + # Invariant restored + dict rekeyed. + assert track.sid == "NEW" + assert "NEW" in local._track_publications + assert "OLD" not in local._track_publications + + # Existing attached processor was re-pushed with the NEW sid (non-empty). + assert processor.stream_info_calls[-1] == { + "room_name": "room-1", + "participant_identity": "agent", + "publication_sid": "NEW", + } + + # Regression guard: a stream created AFTER republish also resolves NEW + # (the exact path from the bug report — stale track.sid would yield ""). + processor2 = _RecordingProcessor() + _stream2 = _make_stream(track=track, processor=processor2) # noqa: F841 + assert processor2.stream_info_calls[-1] == { + "room_name": "room-1", + "participant_identity": "agent", + "publication_sid": "NEW", + } + + +@pytest.mark.asyncio +async def test_repeated_local_track_subscribed_event_does_not_raise() -> None: + """A full reconnect can repeat local_track_subscribed for a publication + whose first-subscription future is already complete. + """ + room = _make_room() + local = _make_local_participant("agent") + room._local_participant = local + + track = _make_track(sid="TR_1") + publication = _make_local_publication(sid="TR_1") + publication._track = track + publication._first_subscription = asyncio.Future() + local._track_publications["TR_1"] = publication + + subscribed_tracks: list[rtc.Track | None] = [] + + @room.on("local_track_subscribed") + def _on_local_track_subscribed(subscribed_track: rtc.Track | None) -> None: + subscribed_tracks.append(subscribed_track) + + event = proto_room.RoomEvent( + local_track_subscribed=proto_room.LocalTrackSubscribed(track_sid="TR_1") + ) + room._on_room_event(event) + await publication.wait_for_subscription() + room._on_room_event(event) + + assert publication._first_subscription.done() + assert subscribed_tracks == [track, track] + + +# -- unpublish_track / room-event race ---------------------------------------- + + +@pytest.mark.asyncio +async def test_unpublish_track_clears_processor_when_it_wins_the_event_race( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """`LocalParticipant.unpublish_track` races the `local_track_unpublished` + room event. When unpublish wins, the room-event handler later finds the + publication already gone and skips its `_set_room(None)`. The unpublish path + must therefore clear the processor itself, otherwise an attached + FrameProcessor never receives the cleared callbacks (and the token_refreshed + listener leaks). This drives unpublish_track with a mocked FFI round-trip and + asserts the clearing happened without any room event firing. + """ + room = _make_room(name="room-1", token="tok-1", url="wss://r") + local = _make_local_participant("agent") + local._ffi_handle = cast(Any, SimpleNamespace(handle=1)) + local._room_queue = BroadcastQueue() + room._local_participant = local + + track = _make_track(sid="OLD") + publication = _make_local_publication(sid="OLD") + publication._track = track + local._track_publications["OLD"] = publication + track._set_room(room) + + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + cleared_info_before = processor.stream_info_cleared_calls + cleared_creds_before = processor.credentials_cleared_calls + + # unpublish_track subscribes to _room_queue BEFORE calling request(), so a + # mocked request that broadcasts the matching callback makes wait_for resolve + # deterministically. The local_track_unpublished room event is never emitted + # here — this simulates unpublish_track winning the race. + resp = proto_ffi.FfiResponse() + resp.unpublish_track.async_id = 1 + cb = proto_ffi.FfiEvent() + cb.unpublish_track.async_id = 1 + + def fake_request(req: proto_ffi.FfiRequest) -> proto_ffi.FfiResponse: + local._room_queue.put_nowait(cb) + return resp + + monkeypatch.setattr(FfiClient.instance, "request", fake_request) + + await local.unpublish_track("OLD") + + assert "OLD" not in local._track_publications + assert publication.track is None + # the unpublish path cleared the processor's room context even though the + # room-event handler never ran + assert processor.stream_info_cleared_calls == cleared_info_before + 1 + assert processor.credentials_cleared_calls == cleared_creds_before + 1 + + +def test_set_room_none_is_idempotent_for_cleared_callbacks() -> None: + room = _make_room(name="room-1", token="tok-1", url="wss://r") + _attach_publication(room, identity="alice", track_sid="TR_1", pub_sid="PUB_1") + track = _make_track(sid="TR_1") + track._set_room(room) + processor = _RecordingProcessor() + _stream = _make_stream(track=track, processor=processor) # noqa: F841 + + track._set_room(None) # first clear (e.g. room event handler) + track._set_room(None) # second clear (e.g. unpublish_track on the same track) + + assert processor.stream_info_cleared_calls == 1 + assert processor.credentials_cleared_calls == 1 + + +def test_local_track_unpublished_event_nulls_publication_track() -> None: + """The `local_track_unpublished` room handler nulls + `unpublished._track` (mirroring `track_unsubscribed`) after clearing the + track's room. If only the room event fires (server-side removal with no + explicit `unpublish_track` call), the publication must not retain a stale + reference to the track. + """ + room = _make_room(name="room-1", token="tok-1", url="wss://r") + local = _make_local_participant("agent") + room._local_participant = local + + track = _make_track(sid="TR_1") + publication = _make_local_publication(sid="TR_1") + publication._track = track + local._track_publications["TR_1"] = publication + track._set_room(room) + + room._on_room_event( + proto_room.RoomEvent( + local_track_unpublished=proto_room.LocalTrackUnpublished(publication_sid="TR_1"), + ) + ) + + assert "TR_1" not in local._track_publications + assert publication.track is None + + +def test_local_track_unpublished_callback_still_sees_track() -> None: + """Backwards-compat: `publication._track` is nulled AFTER the + `local_track_unpublished` event is emitted, so a callback that reads + `publication.track` during the event still observes the track (matching + pre-PR behavior), while the reference is dropped once the handler returns. + """ + room = _make_room(name="room-1", token="tok-1", url="wss://r") + local = _make_local_participant("agent") + room._local_participant = local + + track = _make_track(sid="TR_1") + publication = _make_local_publication(sid="TR_1") + publication._track = track + local._track_publications["TR_1"] = publication + track._set_room(room) + + seen_track: list[Optional[rtc.Track]] = [] + + @room.on("local_track_unpublished") + def _on_unpublished(pub: rtc.LocalTrackPublication) -> None: + seen_track.append(pub.track) + + room._on_room_event( + proto_room.RoomEvent( + local_track_unpublished=proto_room.LocalTrackUnpublished(publication_sid="TR_1"), + ) + ) + + # the callback saw the track (backwards-compatible payload) ... + assert seen_track == [track] + # ... and the reference is dropped once the handler returns + assert publication.track is None + + +def test_token_refresh_listener_only_removed_by_set_room_none() -> None: + """The `token_refreshed` listener a Track registers on its + Room is only ever removed by `_set_room(None)`. `Room.disconnect()` does not + walk tracks to clear them, so the listener (and the Room's strong reference to + the Track's bound method) lingers until the Room itself is collected. This + pins the listener lifecycle the leak stems from. + """ + room = _make_room(name="room-1", token="tok-1", url="wss://r") + track = _make_track(sid="TR_1") + + def _token_listeners(r: rtc.Room) -> int: + return len(r._events.get("token_refreshed", set())) + + track._set_room(room) + assert _token_listeners(room) == 1 + + # Only _set_room(None) detaches it. Nothing in disconnect() does this today. + track._set_room(None) + assert _token_listeners(room) == 0 diff --git a/livekit-rtc/tests/test_change_video_quality.py b/livekit-rtc/tests/test_change_video_quality.py new file mode 100644 index 00000000..9e2b2e2a --- /dev/null +++ b/livekit-rtc/tests/test_change_video_quality.py @@ -0,0 +1,271 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""End-to-end Test for simulcast / SVC video quality layers. + +The test publishes a 1280x720 video track (rolling colored bars) with four +codec configurations and, on the receiver side, verifies that subscribing +to each quality layer (HIGH=f, MEDIUM=h, LOW=q) yields frames of the +expected resolution. + +Codecs covered: + - VP8 (simulcast: three independent encoders) + - H264 (simulcast: three independent encoders) + - H265 (simulcast: three independent encoders, hardware-only on most platforms) + - VP9 (SVC: single bitstream with three spatial layers) + - AV1 (SVC: single bitstream with three spatial layers) + +For VP9 and AV1, libwebrtc converts the multiple RtpEncodingParameters +produced by `simulcast=True` into SVC spatial layers, so the SFU still +exposes the same f/h/q quality switches. + +Requires the following environment variables to run: + LIVEKIT_URL + LIVEKIT_API_KEY + LIVEKIT_API_SECRET +""" + +from __future__ import annotations + +import asyncio +import os +import sys +import time +from typing import Optional, Tuple + +import numpy as np +import pytest + +from livekit import rtc +from livekit.rtc._proto.track_publication_pb2 import VideoQuality + +from utils import ( # type: ignore[import-not-found] + await_event, + create_token, + ensure_rooms_all_connected, + ensure_track_subscribed, + expect_room_event, + skip_if_no_credentials, + unique_room_name, +) + + +PUBLISH_WIDTH = 1280 +PUBLISH_HEIGHT = 720 +PUBLISH_FPS = 15 + +# Default simulcast layer dimensions for a 720p source publication. +LAYER_DIMENSIONS = { + "f": (1280, 720), + "h": (640, 360), + "q": (320, 180), +} + +QUALITY_SEQUENCE = [ + (VideoQuality.VIDEO_QUALITY_HIGH, "f"), + (VideoQuality.VIDEO_QUALITY_LOW, "q"), + (VideoQuality.VIDEO_QUALITY_MEDIUM, "h"), +] + + +def _make_rolling_i420(width: int, height: int, t: float) -> rtc.VideoFrame: + """Build a 1280x720 I420 frame containing 8 vertical color bars that scroll + horizontally over time, so the encoder always sees motion.""" + bar_w = max(width // 8, 1) + offset = int(t * 240) % bar_w + + cols_y = np.arange(width, dtype=np.int32) + bar_idx_y = ((cols_y + offset) // bar_w) % 8 + y_row = (bar_idx_y * 28 + 32).astype(np.uint8) + + cw = width // 2 + cols_c = np.arange(cw, dtype=np.int32) + bar_idx_c = (((cols_c * 2) + offset) // bar_w) % 8 + u_row = (bar_idx_c * 18 + 80).astype(np.uint8) + v_row = (220 - bar_idx_c * 18).astype(np.uint8) + + y_plane = np.tile(y_row, (height, 1)) + u_plane = np.tile(u_row, (height // 2, 1)) + v_plane = np.tile(v_row, (height // 2, 1)) + + data = np.concatenate([y_plane.ravel(), u_plane.ravel(), v_plane.ravel()]) + return rtc.VideoFrame(width, height, rtc.VideoBufferType.I420, data.tobytes()) + + +async def _publish_loop(source: rtc.VideoSource, stop: asyncio.Event) -> None: + interval = 1.0 / PUBLISH_FPS + start = time.monotonic() + while not stop.is_set(): + t = time.monotonic() - start + frame = _make_rolling_i420(PUBLISH_WIDTH, PUBLISH_HEIGHT, t) + source.capture_frame(frame) + try: + await asyncio.wait_for(stop.wait(), timeout=interval) + except asyncio.TimeoutError: + pass + + +async def _wait_for_layer( + stream: rtc.VideoStream, + expected_w: int, + expected_h: int, + *, + timeout: float = 20.0, + samples: int = 5, + tolerance: float = 0.20, +) -> Tuple[int, int]: + """Drain frames until we observe `samples` consecutive frames whose + dimensions match the expected layer (within `tolerance`).""" + deadline = asyncio.get_running_loop().time() + timeout + matches = 0 + last: Optional[Tuple[int, int]] = None + iterator = stream.__aiter__() + while asyncio.get_running_loop().time() < deadline: + try: + ev = await asyncio.wait_for(iterator.__anext__(), timeout=2.0) + except asyncio.TimeoutError: + continue + except StopAsyncIteration: + break + w, h = ev.frame.width, ev.frame.height + last = (w, h) + if ( + abs(w - expected_w) / expected_w <= tolerance + and abs(h - expected_h) / expected_h <= tolerance + ): + matches += 1 + if matches >= samples: + return last + else: + matches = 0 + raise AssertionError(f"timed out waiting for ~{expected_w}x{expected_h}, last seen={last}") + + +# H265 is hardware-only on most platforms and unreliable in CI — opt in via +# LIVEKIT_TEST_H265=1. H264 on macOS depends on VideoToolbox being available +# to the CI runner, which we cannot assume — opt in via LIVEKIT_TEST_H264_MACOS=1. +_H265_ENABLED = os.getenv("LIVEKIT_TEST_H265") == "1" +_H264_MACOS_ENABLED = os.getenv("LIVEKIT_TEST_H264_MACOS") == "1" +_IS_MACOS = sys.platform == "darwin" + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +@pytest.mark.parametrize( + "video_codec, codec_name, mode", + [ + (rtc.VideoCodec.VP8, "vp8", "simulcast"), + pytest.param( + rtc.VideoCodec.H264, + "h264", + "simulcast", + marks=pytest.mark.skipif( + _IS_MACOS and not _H264_MACOS_ENABLED, + reason="H264 disabled on macOS by default; set LIVEKIT_TEST_H264_MACOS=1 to enable", + ), + ), + pytest.param( + rtc.VideoCodec.H265, + "h265", + "simulcast", + marks=pytest.mark.skipif( + not _H265_ENABLED, + reason="H265 disabled by default; set LIVEKIT_TEST_H265=1 to enable", + ), + ), + (rtc.VideoCodec.VP9, "vp9", "svc"), + (rtc.VideoCodec.AV1, "av1", "svc"), + ], +) +async def test_simulcast_quality_layers( + video_codec: rtc.VideoCodec.ValueType, codec_name: str, mode: str +) -> None: + room_name = unique_room_name(f"py-{mode}-{codec_name}") + url = os.environ["LIVEKIT_URL"] + + sender, receiver = rtc.Room(), rtc.Room() + await sender.connect(url, create_token("sender", room_name)) + await receiver.connect(url, create_token("receiver", room_name)) + await ensure_rooms_all_connected([sender, receiver]) + + source = rtc.VideoSource(PUBLISH_WIDTH, PUBLISH_HEIGHT) + track = rtc.LocalVideoTrack.create_video_track(f"{mode}-{codec_name}", source) + options = rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + simulcast=True, + video_codec=video_codec, + video_encoding=rtc.VideoEncoding(max_bitrate=6_000_000, max_framerate=PUBLISH_FPS), + ) + # For SVC codecs, ask libwebrtc to emit three spatial × three temporal + # layers in a single bitstream. Without an explicit scalability_mode AV1 + # falls back to a single-layer stream and the SFU has nothing to switch + # down to. VP9 happens to work without it, but setting it is more + # predictable. + if mode == "svc": + options.scalability_mode = "L3T3_KEY" + + stop = asyncio.Event() + pub_task = asyncio.create_task(_publish_loop(source, stop)) + + stream: Optional[rtc.VideoStream] = None + try: + track_published = expect_room_event( + receiver, + "track_published", + predicate=lambda pub, _p: pub.kind == rtc.TrackKind.KIND_VIDEO, + ) + local_pub = await sender.local_participant.publish_track(track, options) + await await_event(track_published) + + print( + f"[{codec_name}] local_pub: sid={local_pub.sid} " + f"simulcasted={local_pub.simulcasted} " + f"mime_type={local_pub.mime_type} " + f"{local_pub.width}x{local_pub.height}" + ) + remote_pub = await ensure_track_subscribed(receiver, local_pub.sid) + assert remote_pub.track is not None + print( + f"[{codec_name}] remote_pub: sid={remote_pub.sid} " + f"simulcasted={remote_pub.simulcasted} " + f"mime_type={remote_pub.mime_type} " + f"{remote_pub.width}x{remote_pub.height}" + ) + + stream = rtc.VideoStream.from_track(track=remote_pub.track) + + # Bandwidth can be unstable right after subscribe — explicitly request + # the top layer and wait until we see HIGH-resolution frames before + # starting the quality-switching assertions. Use a longer timeout + # here since the encoder may need extra time to ramp up to f. + remote_pub.set_video_quality(VideoQuality.VIDEO_QUALITY_HIGH) + ew, eh = LAYER_DIMENSIONS["f"] + warmup = await _wait_for_layer(stream, ew, eh, timeout=40.0) + print(f"[{codec_name}] warmup HIGH/f got={warmup[0]}x{warmup[1]}") + + for quality, layer in QUALITY_SEQUENCE: + remote_pub.set_video_quality(quality) + ew, eh = LAYER_DIMENSIONS[layer] + actual = await _wait_for_layer(stream, ew, eh, timeout=20.0) + print(f"[{codec_name}] layer={layer} expected~{ew}x{eh} got={actual[0]}x{actual[1]}") + finally: + stop.set() + try: + await pub_task + except Exception as e: + raise AssertionError(f"pub_task failed: {e}") from e + if stream is not None: + await stream.aclose() + await sender.disconnect() + await receiver.disconnect() diff --git a/livekit-rtc/tests/test_chat.py b/livekit-rtc/tests/test_chat.py deleted file mode 100644 index 161bf9be..00000000 --- a/livekit-rtc/tests/test_chat.py +++ /dev/null @@ -1,32 +0,0 @@ -from datetime import datetime -import json - -from livekit.rtc import ChatMessage - - -def test_message_basics(): - msg = ChatMessage() - assert msg.id is not None, "message id should be set" - assert msg.timestamp is not None, "timestamp should be set" - assert msg.timestamp.day == datetime.now().day, "timestamp should be today" - assert len(msg.id) > 5, "message id should be long enough" - - -def test_message_serialization(): - msg = ChatMessage( - message="hello", - ) - data = msg.asjsondict() - msg2 = ChatMessage.from_jsondict(json.loads(json.dumps(data))) - assert msg2.message == msg.message, "message should be the same" - assert msg2.id == msg.id, "id should be the same" - assert int(msg2.timestamp.timestamp() / 1000) == int( - msg.timestamp.timestamp() / 1000 - ), "timestamp should be the same" - assert not msg2.deleted, "not deleted" - - # deletion is handled - msg.deleted = True - data = msg.asjsondict() - msg2 = ChatMessage.from_jsondict(json.loads(json.dumps(data))) - assert msg2.deleted, "should be deleted" diff --git a/livekit-rtc/tests/test_dc.py b/livekit-rtc/tests/test_dc.py new file mode 100644 index 00000000..aff64e3f --- /dev/null +++ b/livekit-rtc/tests/test_dc.py @@ -0,0 +1,279 @@ +"""End-to-end Test for data-channel scenarios. + +Covers one-to-one delivery, broadcasting to all, topic filtering, and targeted +delivery via `destination_identities`. + +Requires the following environment variables to run: + LIVEKIT_URL + LIVEKIT_API_KEY + LIVEKIT_API_SECRET +""" + +from __future__ import annotations + +import asyncio +from typing import Optional + +import pytest + +from livekit import rtc + +from utils import ( # type: ignore[import-not-found] + await_event, + connect_room, + ensure_participants_visible, + ensure_rooms_all_connected, + expect_room_event, + skip_if_no_credentials, + unique_room_name, + wait_until, +) + + +class _DataCollector: + """Collects `data_received` packets matching `sender_identity` (when set).""" + + def __init__(self, room: rtc.Room, sender_identity: Optional[str] = None) -> None: + self.packets: list[rtc.DataPacket] = [] + self._sender_identity = sender_identity + + def _on_data(packet: rtc.DataPacket) -> None: + if self._sender_identity is not None and ( + packet.participant is None or packet.participant.identity != self._sender_identity + ): + return + self.packets.append(packet) + + room.on("data_received", _on_data) + + def payloads(self) -> list[bytes]: + return [p.data for p in self.packets] + + def topics(self) -> list[str | None]: + return [p.topic for p in self.packets] + + +async def _assert_no_data( + room: rtc.Room, collector: _DataCollector, *, settle: float = 1.0 +) -> None: + """Give the server time to deliver, then assert nothing arrived.""" + await asyncio.sleep(settle) + assert collector.packets == [], ( + f"{room.local_participant.identity} unexpectedly received " + f"{len(collector.packets)} packet(s): {collector.payloads()}" + ) + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +async def test_data_one_to_one() -> None: + """sender targets a single identity; only that identity receives.""" + room_name = unique_room_name("py-dc-1to1") + + sender = rtc.Room() + receiver = rtc.Room() + bystander = rtc.Room() + + await connect_room("sender", room_name, room=sender) + await connect_room("receiver", room_name, room=receiver) + await connect_room("bystander", room_name, room=bystander) + await ensure_rooms_all_connected([sender, receiver, bystander]) + await ensure_participants_visible(sender, ["receiver", "bystander"]) + + receiver_collector = _DataCollector(receiver, sender_identity="sender") + bystander_collector = _DataCollector(bystander, sender_identity="sender") + + receiver_got = expect_room_event( + receiver, + "data_received", + predicate=lambda packet: ( + packet.participant is not None and packet.participant.identity == "sender" + ), + ) + + payload = b"hello receiver" + await sender.local_participant.publish_data(payload, destination_identities=["receiver"]) + + await await_event(receiver_got) + assert receiver_collector.payloads() == [payload] + await _assert_no_data(bystander, bystander_collector) + + await asyncio.gather(sender.disconnect(), receiver.disconnect(), bystander.disconnect()) + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +async def test_data_one_to_many_targeted() -> None: + """sender targets a subset of identities; only that subset receives.""" + room_name = unique_room_name("py-dc-1tomany") + + sender = rtc.Room() + r1 = rtc.Room() + r2 = rtc.Room() + excluded = rtc.Room() + + await connect_room("sender", room_name, room=sender) + await connect_room("r1", room_name, room=r1) + await connect_room("r2", room_name, room=r2) + await connect_room("excluded", room_name, room=excluded) + await ensure_rooms_all_connected([sender, r1, r2, excluded]) + await ensure_participants_visible(sender, ["r1", "r2", "excluded"]) + + r1_collector = _DataCollector(r1, sender_identity="sender") + r2_collector = _DataCollector(r2, sender_identity="sender") + excluded_collector = _DataCollector(excluded, sender_identity="sender") + + r1_got = expect_room_event( + r1, + "data_received", + predicate=lambda packet: ( + packet.participant is not None and packet.participant.identity == "sender" + ), + ) + r2_got = expect_room_event( + r2, + "data_received", + predicate=lambda packet: ( + packet.participant is not None and packet.participant.identity == "sender" + ), + ) + + payload = b"hello selected" + await sender.local_participant.publish_data(payload, destination_identities=["r1", "r2"]) + + await asyncio.gather(await_event(r1_got), await_event(r2_got)) + assert r1_collector.payloads() == [payload] + assert r2_collector.payloads() == [payload] + await _assert_no_data(excluded, excluded_collector) + + await asyncio.gather( + sender.disconnect(), r1.disconnect(), r2.disconnect(), excluded.disconnect() + ) + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +async def test_data_broadcast() -> None: + """Empty `destination_identities` broadcasts to every other participant.""" + room_name = unique_room_name("py-dc-broadcast") + + sender = rtc.Room() + receivers = [rtc.Room() for _ in range(3)] + receiver_idents = [f"r{i}" for i in range(len(receivers))] + + await connect_room("sender", room_name, room=sender) + for room, ident in zip(receivers, receiver_idents): + await connect_room(ident, room_name, room=room) + await ensure_rooms_all_connected([sender, *receivers]) + await ensure_participants_visible(sender, receiver_idents) + + collectors = [_DataCollector(room, sender_identity="sender") for room in receivers] + received_futures = [ + expect_room_event( + room, + "data_received", + predicate=lambda packet: ( + packet.participant is not None and packet.participant.identity == "sender" + ), + ) + for room in receivers + ] + + payload = b"hello everyone" + await sender.local_participant.publish_data(payload) + + await asyncio.gather(*(await_event(f) for f in received_futures)) + for ident, collector in zip(receiver_idents, collectors): + assert collector.payloads() == [payload], f"{ident} payloads mismatch" + + await asyncio.gather(sender.disconnect(), *(r.disconnect() for r in receivers)) + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +async def test_data_topic_passthrough() -> None: + """Topic field is preserved end-to-end and observable by every receiver.""" + room_name = unique_room_name("py-dc-topic") + + sender = rtc.Room() + r1 = rtc.Room() + r2 = rtc.Room() + + await connect_room("sender", room_name, room=sender) + await connect_room("r1", room_name, room=r1) + await connect_room("r2", room_name, room=r2) + await ensure_rooms_all_connected([sender, r1, r2]) + await ensure_participants_visible(sender, ["r1", "r2"]) + + r1_collector = _DataCollector(r1, sender_identity="sender") + r2_collector = _DataCollector(r2, sender_identity="sender") + + # Send three messages: two on "chat", one on "telemetry". + messages = [ + (b"chat-1", "chat"), + (b"telemetry-1", "telemetry"), + (b"chat-2", "chat"), + ] + + def _all_received(collector: _DataCollector) -> bool: + return len(collector.packets) >= len(messages) + + for payload, topic in messages: + await sender.local_participant.publish_data(payload, topic=topic) + + await wait_until( + lambda: _all_received(r1_collector) and _all_received(r2_collector), + message="receivers did not get all topic messages", + ) + + expected_pairs = [(payload, topic) for payload, topic in messages] + for collector, ident in [(r1_collector, "r1"), (r2_collector, "r2")]: + got = list(zip(collector.payloads(), collector.topics())) + assert got == expected_pairs, f"{ident} mismatch: expected {expected_pairs}, got {got}" + + # Also verify `chat`-only filtering at the consumer side works as expected. + chat_only_r1 = [p for p in r1_collector.packets if p.topic == "chat"] + assert [p.data for p in chat_only_r1] == [b"chat-1", b"chat-2"] + + await asyncio.gather(sender.disconnect(), r1.disconnect(), r2.disconnect()) + + +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.asyncio +async def test_data_targeted_with_topic() -> None: + """Targeted send carries the topic; non-targets receive nothing.""" + room_name = unique_room_name("py-dc-targeted-topic") + + sender = rtc.Room() + target = rtc.Room() + other = rtc.Room() + + await connect_room("sender", room_name, room=sender) + await connect_room("target", room_name, room=target) + await connect_room("other", room_name, room=other) + await ensure_rooms_all_connected([sender, target, other]) + await ensure_participants_visible(sender, ["target", "other"]) + + target_collector = _DataCollector(target, sender_identity="sender") + other_collector = _DataCollector(other, sender_identity="sender") + + target_got = expect_room_event( + target, + "data_received", + predicate=lambda packet: ( + packet.participant is not None and packet.participant.identity == "sender" + ), + ) + + payload = b"private ping" + topic = "private" + await sender.local_participant.publish_data( + payload, destination_identities=["target"], topic=topic + ) + + await await_event(target_got) + assert target_collector.payloads() == [payload] + assert target_collector.topics() == [topic] + await _assert_no_data(other, other_collector) + + await asyncio.gather(sender.disconnect(), target.disconnect(), other.disconnect()) diff --git a/livekit-rtc/tests/test_e2ee.py b/livekit-rtc/tests/test_e2ee.py new file mode 100644 index 00000000..47a192d7 --- /dev/null +++ b/livekit-rtc/tests/test_e2ee.py @@ -0,0 +1,202 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Unit tests for E2EE functionality.""" + + +class TestKeyProviderOptions: + """Tests for KeyProviderOptions dataclass.""" + + def test_default_values(self) -> None: + """Test that KeyProviderOptions has correct default values.""" + from livekit.rtc.e2ee import ( + KeyProviderOptions, + DEFAULT_RATCHET_SALT, + DEFAULT_RATCHET_WINDOW_SIZE, + DEFAULT_FAILURE_TOLERANCE, + DEFAULT_KEY_RING_SIZE, + ) + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + options = KeyProviderOptions() + + assert options.shared_key is None + assert options.ratchet_salt == DEFAULT_RATCHET_SALT + assert options.ratchet_window_size == DEFAULT_RATCHET_WINDOW_SIZE + assert options.failure_tolerance == DEFAULT_FAILURE_TOLERANCE + assert options.key_ring_size == DEFAULT_KEY_RING_SIZE + assert options.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2 + + def test_custom_values(self) -> None: + """Test KeyProviderOptions with custom values.""" + from livekit.rtc.e2ee import KeyProviderOptions + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + options = KeyProviderOptions( + shared_key=b"my-secret-key", + ratchet_salt=b"custom-salt", + ratchet_window_size=32, + failure_tolerance=5, + key_ring_size=8, + key_derivation_function=proto_e2ee.KeyDerivationFunction.HKDF, + ) + + assert options.shared_key == b"my-secret-key" + assert options.ratchet_salt == b"custom-salt" + assert options.ratchet_window_size == 32 + assert options.failure_tolerance == 5 + assert options.key_ring_size == 8 + assert options.key_derivation_function == proto_e2ee.KeyDerivationFunction.HKDF + + def test_various_key_lengths(self) -> None: + """Test that shared_key accepts various lengths.""" + from livekit.rtc.e2ee import KeyProviderOptions + + # Short key + options_short = KeyProviderOptions(shared_key=b"short") + assert options_short.shared_key == b"short" + + # Medium key + options_medium = KeyProviderOptions(shared_key=b"medium-length-key-here") + assert options_medium.shared_key == b"medium-length-key-here" + + # Long key + long_key = b"a" * 256 + options_long = KeyProviderOptions(shared_key=long_key) + assert options_long.shared_key == long_key + + # Binary key + binary_key = bytes(range(256)) + options_binary = KeyProviderOptions(shared_key=binary_key) + assert options_binary.shared_key == binary_key + + +class TestE2EEOptions: + """Tests for E2EEOptions dataclass.""" + + def test_default_values(self) -> None: + """Test E2EEOptions default values.""" + from livekit.rtc.e2ee import E2EEOptions, KeyProviderOptions + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + options = E2EEOptions() + + assert isinstance(options.key_provider_options, KeyProviderOptions) + assert options.encryption_type == proto_e2ee.EncryptionType.GCM + + def test_with_shared_key(self) -> None: + """Test E2EEOptions with a shared key.""" + from livekit.rtc.e2ee import E2EEOptions, KeyProviderOptions + + key_options = KeyProviderOptions(shared_key=b"test-key") + options = E2EEOptions(key_provider_options=key_options) + + assert options.key_provider_options.shared_key == b"test-key" + + +class TestProtoMessageBuilding: + """Tests for proto message building with E2EE options.""" + + def test_proto_key_provider_options_fields(self) -> None: + """Test that proto KeyProviderOptions has all required fields.""" + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + proto_options = proto_e2ee.KeyProviderOptions() + + # Set all fields that should be present + proto_options.shared_key = b"test-key" + proto_options.ratchet_window_size = 16 + proto_options.ratchet_salt = b"LKFrameEncryptionKey" + proto_options.failure_tolerance = -1 + proto_options.key_ring_size = 16 + proto_options.key_derivation_function = proto_e2ee.KeyDerivationFunction.PBKDF2 + + # Verify fields are set correctly + assert proto_options.shared_key == b"test-key" + assert proto_options.ratchet_window_size == 16 + assert proto_options.ratchet_salt == b"LKFrameEncryptionKey" + assert proto_options.failure_tolerance == -1 + assert proto_options.key_ring_size == 16 + assert proto_options.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2 + + def test_proto_serialization(self) -> None: + """Test that proto message can be serialized without errors.""" + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + proto_options = proto_e2ee.KeyProviderOptions() + proto_options.ratchet_window_size = 16 + proto_options.ratchet_salt = b"LKFrameEncryptionKey" + proto_options.failure_tolerance = -1 + proto_options.key_ring_size = 16 + proto_options.key_derivation_function = proto_e2ee.KeyDerivationFunction.PBKDF2 + + # This should not raise an EncodeError + serialized = proto_options.SerializeToString() + assert len(serialized) > 0 + + # Verify we can deserialize it back + parsed = proto_e2ee.KeyProviderOptions() + parsed.ParseFromString(serialized) + assert parsed.key_ring_size == 16 + assert parsed.key_derivation_function == proto_e2ee.KeyDerivationFunction.PBKDF2 + + def test_e2ee_options_proto_serialization(self) -> None: + """Test full E2eeOptions proto serialization.""" + from livekit.rtc._proto import e2ee_pb2 as proto_e2ee + + e2ee_opts = proto_e2ee.E2eeOptions() + e2ee_opts.encryption_type = proto_e2ee.EncryptionType.GCM + e2ee_opts.key_provider_options.shared_key = b"my-shared-key" + e2ee_opts.key_provider_options.ratchet_window_size = 16 + e2ee_opts.key_provider_options.ratchet_salt = b"LKFrameEncryptionKey" + e2ee_opts.key_provider_options.failure_tolerance = -1 + e2ee_opts.key_provider_options.key_ring_size = 16 + e2ee_opts.key_provider_options.key_derivation_function = ( + proto_e2ee.KeyDerivationFunction.PBKDF2 + ) + + # This should not raise an EncodeError + serialized = e2ee_opts.SerializeToString() + assert len(serialized) > 0 + + +class TestPublicExports: + """Tests for public API exports.""" + + def test_key_derivation_function_exported(self) -> None: + """Test that KeyDerivationFunction is exported from the package.""" + from livekit.rtc import KeyDerivationFunction + + # Verify enum values are accessible + assert KeyDerivationFunction.PBKDF2 == 0 + assert KeyDerivationFunction.HKDF == 1 + + def test_encryption_type_exported(self) -> None: + """Test that EncryptionType is exported from the package.""" + from livekit.rtc import EncryptionType + + assert EncryptionType.NONE == 0 + assert EncryptionType.GCM == 1 + assert EncryptionType.CUSTOM == 2 + + def test_e2ee_classes_exported(self) -> None: + """Test that E2EE classes are exported from the package.""" + from livekit.rtc import E2EEOptions, KeyProviderOptions + + # Should be able to instantiate without errors + key_opts = KeyProviderOptions() + e2ee_opts = E2EEOptions() + + assert key_opts is not None + assert e2ee_opts is not None diff --git a/livekit-rtc/tests/test_e2ee_per_participant.py b/livekit-rtc/tests/test_e2ee_per_participant.py new file mode 100644 index 00000000..ed5da639 --- /dev/null +++ b/livekit-rtc/tests/test_e2ee_per_participant.py @@ -0,0 +1,348 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""E2E tests for E2EE key per-partcipant.""" + +from __future__ import annotations + +import asyncio +import os + +import pytest + +from livekit import rtc + +from utils import ( # type: ignore[import-not-found] + assert_eventually, + create_token, + publish_dummy_video, + skip_if_no_credentials, + unique_room_name, +) + + +# Per-participant keys (publisher identity → list of (key_bytes, key_index)) +PUBLISHER_IDENTITY = "e2eePublisher" +PUBLISHER_KEYS: list[tuple[bytes, int]] = [ + (b"12345678", 0), + (b"abcdefgh", 1), + (b"ijklmnop", 2), +] + +WIDTH, HEIGHT = 320, 180 +FRAME_RATE = 15 + + +def make_per_participant_e2ee_options() -> rtc.E2EEOptions: + options = rtc.E2EEOptions() + # No shared key — keys are installed per participant after connect. + options.key_provider_options.shared_key = None + options.key_provider_options.ratchet_window_size = 16 + # failure_tolerance must be >= 0 for the cryptor to surface DECRYPTION_FAILED; + # the default -1 means "infinite retries via auto-ratchet" and never emits. + options.key_provider_options.failure_tolerance = 3 + return options + + +def install_publisher_keys(room: rtc.Room) -> None: + """Install the publisher's per-participant keys on `room`'s key provider.""" + key_provider = room.e2ee_manager.key_provider + assert key_provider is not None, "room is missing a key provider" + for key, key_index in PUBLISHER_KEYS: + key_provider.set_key(PUBLISHER_IDENTITY, key, key_index) + + +def set_key_index_on_all_cryptors(room: rtc.Room, key_index: int) -> None: + """Equivalent of dart's e2eeManager.setKeyIndex(idx): apply to every cryptor.""" + for cryptor in room.e2ee_manager.frame_cryptors(): + cryptor.set_key_index(key_index) + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_e2ee_per_participant() -> None: + """E2E test for per-participant E2EE keys. + + E2EE per-participant E2E test: + 1. Each room has its own KeyProvider; the publisher's three keys + (indexes 0/1/2) are installed on every room. + 2. Publisher publishes a video track. Receivers must observe + track_published and an OK e2ee_state. + 3. All participants must use GCM encryption. + 4. Publisher ratchets key index 2 → receivers observe KEY_RATCHETED. + 5. Publisher overwrites key index 0 with a wrong key → receivers + observe DECRYPTION_FAILED. + 6. Publisher switches to key index 1 → receivers observe OK + (since they already have key index 1). + 7. Publisher switches to key index 2 and ratchets it; receivers + switch to key index 2 → receivers observe KEY_RATCHETED. + """ + room_name = unique_room_name("test-e2ee-per-participant") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + receiver1_room = rtc.Room() + receiver2_room = rtc.Room() + + publisher_token = create_token(PUBLISHER_IDENTITY, room_name) + receiver1_token = create_token("receiver1", room_name) + receiver2_token = create_token("receiver2", room_name) + + track_published_on: dict[str, asyncio.Event] = { + "receiver1": asyncio.Event(), + "receiver2": asyncio.Event(), + } + track_subscribed_on: dict[str, asyncio.Event] = { + "receiver1": asyncio.Event(), + "receiver2": asyncio.Event(), + } + seen_e2ee_states: dict[str, set[int]] = { + "receiver1": set(), + "receiver2": set(), + } + e2ee_state_log: dict[str, list[int]] = { + "receiver1": [], + "receiver2": [], + } + + def wire_receiver(room: rtc.Room, label: str) -> None: + @room.on("track_published") + def _on_pub( + publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant + ) -> None: + track_published_on[label].set() + + @room.on("track_subscribed") + def _on_sub( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + track_subscribed_on[label].set() + + @room.on("e2ee_state_changed") + def _on_state(participant: rtc.Participant, state: int) -> None: + if participant is not None and participant.identity == PUBLISHER_IDENTITY: + seen_e2ee_states[label].add(state) + e2ee_state_log[label].append(state) + + wire_receiver(receiver1_room, "receiver1") + wire_receiver(receiver2_room, "receiver2") + + publish_stop = asyncio.Event() + publish_task: asyncio.Task[None] | None = None + + try: + # 1) connect all three rooms with per-participant E2EE options + await publisher_room.connect( + url, + publisher_token, + options=rtc.RoomOptions( + auto_subscribe=True, encryption=make_per_participant_e2ee_options() + ), + ) + await receiver1_room.connect( + url, + receiver1_token, + options=rtc.RoomOptions( + auto_subscribe=True, encryption=make_per_participant_e2ee_options() + ), + ) + await receiver2_room.connect( + url, + receiver2_token, + options=rtc.RoomOptions( + auto_subscribe=True, encryption=make_per_participant_e2ee_options() + ), + ) + + for room in (publisher_room, receiver1_room, receiver2_room): + assert room.connection_state == rtc.ConnectionState.CONN_CONNECTED + install_publisher_keys(room) + + # 2) publish a video track + source = rtc.VideoSource(WIDTH, HEIGHT) + track = rtc.LocalVideoTrack.create_video_track("e2ee-cam", source) + publish_options = rtc.TrackPublishOptions() + publish_options.source = rtc.TrackSource.SOURCE_CAMERA + publish_options.video_codec = rtc.VideoCodec.VP8 + publish_options.simulcast = True + publication = await publisher_room.local_participant.publish_track(track, publish_options) + assert publication is not None and publication.sid + + publish_task = asyncio.create_task(publish_dummy_video(source, publish_stop)) + + # 3) receivers see track_published / track_subscribed + await asyncio.wait_for(track_published_on["receiver1"].wait(), timeout=15.0) + await asyncio.wait_for(track_published_on["receiver2"].wait(), timeout=15.0) + await asyncio.wait_for(track_subscribed_on["receiver1"].wait(), timeout=15.0) + await asyncio.wait_for(track_subscribed_on["receiver2"].wait(), timeout=15.0) + + # 4) receivers reach EncryptionState.OK + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"], + message=( + f"receiver1 did not reach EncryptionState.OK (saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"], + message=( + f"receiver2 did not reach EncryptionState.OK (saw {e2ee_state_log['receiver2']})" + ), + ) + + # 5) verify GCM on both receiver rooms + def all_remote_video_pubs_gcm(room: rtc.Room) -> bool: + for rp in room.remote_participants.values(): + for pub in rp.track_publications.values(): + if pub.kind != rtc.TrackKind.KIND_VIDEO: + continue + if pub.encryption_type != rtc.EncryptionType.GCM: + return False + return True + + await assert_eventually( + lambda: all_remote_video_pubs_gcm(receiver1_room), + message="receiver1 sees a non-GCM video publication", + ) + await assert_eventually( + lambda: all_remote_video_pubs_gcm(receiver2_room), + message="receiver2 sees a non-GCM video publication", + ) + + # 6) ratchet publisher's key at index 0 (the default cryptor key index); + # receivers should auto-ratchet to match. + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + publisher_key_provider = publisher_room.e2ee_manager.key_provider + assert publisher_key_provider is not None + publisher_key_provider.ratchet_key(PUBLISHER_IDENTITY, key_index=0) + + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver1"], + message=( + "receiver1 did not observe KEY_RATCHETED after publisher ratchet " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver2"], + message=( + "receiver2 did not observe KEY_RATCHETED after publisher ratchet " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + # 7) publisher overwrites key index 0 with a wrong key (receivers + # still hold the original key 0) → receivers observe DECRYPTION_FAILED + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + publisher_key_provider.set_key(PUBLISHER_IDENTITY, b"wrongkey", 0) + + await assert_eventually( + lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver1"], + message=( + f"receiver1 did not observe DECRYPTION_FAILED (saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver2"], + message=( + f"receiver2 did not observe DECRYPTION_FAILED (saw {e2ee_state_log['receiver2']})" + ), + ) + + # 8) publisher switches to key index 1 (still correct on both sides). + # Once failure_tolerance is exhausted the receiver cryptor stops + # retrying, so we also re-install key index 1 on the receivers to + # deliver a fresh key event that wakes the cryptor back up. + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + set_key_index_on_all_cryptors(publisher_room, 1) + key1_bytes, _ = PUBLISHER_KEYS[1] + receiver1_key_provider = receiver1_room.e2ee_manager.key_provider + receiver2_key_provider = receiver2_room.e2ee_manager.key_provider + assert receiver1_key_provider is not None + assert receiver2_key_provider is not None + receiver1_key_provider.set_key(PUBLISHER_IDENTITY, key1_bytes, 1) + receiver2_key_provider.set_key(PUBLISHER_IDENTITY, key1_bytes, 1) + + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"], + message=( + "receiver1 did not return to EncryptionState.OK with key index 1 " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"], + message=( + "receiver2 did not return to EncryptionState.OK with key index 1 " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + # 9) publisher switches to key index 2 and ratchets it; receivers + # switch to key index 2 → should observe KEY_RATCHETED + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + set_key_index_on_all_cryptors(publisher_room, 2) + publisher_key_provider.ratchet_key(PUBLISHER_IDENTITY, key_index=2) + set_key_index_on_all_cryptors(receiver1_room, 2) + set_key_index_on_all_cryptors(receiver2_room, 2) + + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver1"], + message=( + "receiver1 did not observe KEY_RATCHETED after key-index switch " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver2"], + message=( + "receiver2 did not observe KEY_RATCHETED after key-index switch " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + finally: + publish_stop.set() + if publish_task is not None: + try: + await asyncio.wait_for(publish_task, timeout=2.0) + except (asyncio.TimeoutError, asyncio.CancelledError): + publish_task.cancel() + + for room in (publisher_room, receiver1_room, receiver2_room): + if room.isconnected(): + await room.disconnect() diff --git a/livekit-rtc/tests/test_e2ee_shared_key.py b/livekit-rtc/tests/test_e2ee_shared_key.py new file mode 100644 index 00000000..230793f9 --- /dev/null +++ b/livekit-rtc/tests/test_e2ee_shared_key.py @@ -0,0 +1,300 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""E2E tests for E2EE shared key .""" + +from __future__ import annotations + +import asyncio +import os + +import pytest + +from livekit import rtc + +from utils import ( # type: ignore[import-not-found] + assert_eventually, + create_token, + publish_dummy_video, + skip_if_no_credentials, + unique_room_name, +) + + +SHARED_KEY = b"12345678" +WRONG_KEY = b"wrongkey" +WIDTH, HEIGHT = 320, 180 +FRAME_RATE = 15 + + +def make_e2ee_options() -> rtc.E2EEOptions: + options = rtc.E2EEOptions() + options.key_provider_options.shared_key = SHARED_KEY + options.key_provider_options.ratchet_window_size = 16 + # failure_tolerance must be >= 0 for the cryptor to surface DECRYPTION_FAILED; + # the default -1 means "infinite retries via auto-ratchet" and never emits. + options.key_provider_options.failure_tolerance = 3 + return options + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_e2ee_shared_key() -> None: + """E2E test for shared-key E2EE. + + E2EE shared key E2E test: + 1. Publisher and two receivers connect with the same shared key. + 2. Publisher publishes a video track; receivers must observe + track_published and an OK e2ee_state. + 3. All participants must use GCM encryption. + 4. Publisher ratchets the shared key; receivers must observe + KEY_RATCHETED state. + 5. Publisher swaps its shared key for a wrong one; receivers must observe + DECRYPTION_FAILED. + 6. After exhausting failure_tolerance, the receiver cryptor stops + retrying; restoring the correct key on the publisher and re-installing + it on the receivers must bring them back to OK. + """ + room_name = unique_room_name("test-e2ee-shared-key") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + receiver1_room = rtc.Room() + receiver2_room = rtc.Room() + + publisher_token = create_token("e2eePublisher", room_name) + receiver1_token = create_token("receiver1", room_name) + receiver2_token = create_token("receiver2", room_name) + + # State tracked from event callbacks + track_published_on: dict[str, asyncio.Event] = { + "receiver1": asyncio.Event(), + "receiver2": asyncio.Event(), + } + track_subscribed_on: dict[str, asyncio.Event] = { + "receiver1": asyncio.Event(), + "receiver2": asyncio.Event(), + } + # all e2ee states seen for the publisher's identity (per receiver), since the + # last reset. KEY_RATCHETED in particular can be transient. + seen_e2ee_states: dict[str, set[int]] = { + "receiver1": set(), + "receiver2": set(), + } + e2ee_state_log: dict[str, list[int]] = { + "receiver1": [], + "receiver2": [], + } + + def wire_receiver(room: rtc.Room, label: str) -> None: + @room.on("track_published") + def _on_pub( + publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant + ) -> None: + track_published_on[label].set() + + @room.on("track_subscribed") + def _on_sub( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + track_subscribed_on[label].set() + + @room.on("e2ee_state_changed") + def _on_state(participant: rtc.Participant, state: int) -> None: + # only track state from the publisher's frames + if participant is not None and participant.identity == "e2eePublisher": + seen_e2ee_states[label].add(state) + e2ee_state_log[label].append(state) + + wire_receiver(receiver1_room, "receiver1") + wire_receiver(receiver2_room, "receiver2") + + publish_stop = asyncio.Event() + publish_task: asyncio.Task[None] | None = None + + try: + connect_options = rtc.RoomOptions(auto_subscribe=True, encryption=make_e2ee_options()) + + await publisher_room.connect(url, publisher_token, options=connect_options) + await receiver1_room.connect( + url, + receiver1_token, + options=rtc.RoomOptions(auto_subscribe=True, encryption=make_e2ee_options()), + ) + await receiver2_room.connect( + url, + receiver2_token, + options=rtc.RoomOptions(auto_subscribe=True, encryption=make_e2ee_options()), + ) + + # 1) all connected + for room in (publisher_room, receiver1_room, receiver2_room): + assert room.connection_state == rtc.ConnectionState.CONN_CONNECTED + + # 2) publish a video track from the publisher + source = rtc.VideoSource(WIDTH, HEIGHT) + track = rtc.LocalVideoTrack.create_video_track("e2ee-cam", source) + publish_options = rtc.TrackPublishOptions() + publish_options.source = rtc.TrackSource.SOURCE_CAMERA + publish_options.video_codec = rtc.VideoCodec.VP8 + publish_options.simulcast = True + publication = await publisher_room.local_participant.publish_track(track, publish_options) + assert publication is not None and publication.sid + + publish_task = asyncio.create_task(publish_dummy_video(source, publish_stop)) + + # 3) receivers must observe track_published + await asyncio.wait_for(track_published_on["receiver1"].wait(), timeout=15.0) + await asyncio.wait_for(track_published_on["receiver2"].wait(), timeout=15.0) + + # 4) receivers must subscribe to the track + await asyncio.wait_for(track_subscribed_on["receiver1"].wait(), timeout=15.0) + await asyncio.wait_for(track_subscribed_on["receiver2"].wait(), timeout=15.0) + + # 5) wait for receivers to see an OK encryption state from the publisher + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"], + timeout=15.0, + message="receiver1 did not reach EncryptionState.OK", + ) + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"], + timeout=15.0, + message="receiver2 did not reach EncryptionState.OK", + ) + + # 6) verify all participants are using GCM on the published video track + def all_remote_video_pubs_gcm(room: rtc.Room) -> bool: + for rp in room.remote_participants.values(): + for pub in rp.track_publications.values(): + if pub.kind != rtc.TrackKind.KIND_VIDEO: + continue + if pub.encryption_type != rtc.EncryptionType.GCM: + return False + return True + + await assert_eventually( + lambda: all_remote_video_pubs_gcm(receiver1_room), + message="receiver1 sees a non-GCM video publication", + ) + await assert_eventually( + lambda: all_remote_video_pubs_gcm(receiver2_room), + message="receiver2 sees a non-GCM video publication", + ) + + # 7) ratchet the shared key on the publisher; receivers should ratchet too + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + key_provider = publisher_room.e2ee_manager.key_provider + assert key_provider is not None + key_provider.ratchet_shared_key(key_index=0) + + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver1"], + timeout=15.0, + message=( + "receiver1 did not observe KEY_RATCHETED after publisher ratchet " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.KEY_RATCHETED in seen_e2ee_states["receiver2"], + timeout=15.0, + message=( + "receiver2 did not observe KEY_RATCHETED after publisher ratchet " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + # 8) swap the publisher's shared key for a wrong one; receivers should + # fail to decrypt and surface DECRYPTION_FAILED once failure_tolerance + # is exhausted. + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + key_provider.set_shared_key(WRONG_KEY, key_index=0) + + await assert_eventually( + lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver1"], + timeout=15.0, + message=( + "receiver1 did not observe DECRYPTION_FAILED after publisher swapped key " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.DECRYPTION_FAILED in seen_e2ee_states["receiver2"], + timeout=15.0, + message=( + "receiver2 did not observe DECRYPTION_FAILED after publisher swapped key " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + # 9) restore the correct shared key on the publisher, and re-install it + # on the receivers too: once failure_tolerance is exhausted the + # receiver cryptor stops retrying, so a fresh key event is required to + # bring it back to OK. + seen_e2ee_states["receiver1"].clear() + seen_e2ee_states["receiver2"].clear() + e2ee_state_log["receiver1"].clear() + e2ee_state_log["receiver2"].clear() + await asyncio.sleep(1.0) + + key_provider.set_shared_key(SHARED_KEY, key_index=0) + receiver1_key_provider = receiver1_room.e2ee_manager.key_provider + receiver2_key_provider = receiver2_room.e2ee_manager.key_provider + assert receiver1_key_provider is not None + assert receiver2_key_provider is not None + receiver1_key_provider.set_shared_key(SHARED_KEY, key_index=0) + receiver2_key_provider.set_shared_key(SHARED_KEY, key_index=0) + + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver1"], + timeout=15.0, + message=( + "receiver1 did not return to EncryptionState.OK after key restore " + f"(saw {e2ee_state_log['receiver1']})" + ), + ) + await assert_eventually( + lambda: rtc.EncryptionState.OK in seen_e2ee_states["receiver2"], + timeout=15.0, + message=( + "receiver2 did not return to EncryptionState.OK after key restore " + f"(saw {e2ee_state_log['receiver2']})" + ), + ) + + finally: + publish_stop.set() + if publish_task is not None: + try: + await asyncio.wait_for(publish_task, timeout=2.0) + except (asyncio.TimeoutError, asyncio.CancelledError): + publish_task.cancel() + + for room in (publisher_room, receiver1_room, receiver2_room): + if room.isconnected(): + await room.disconnect() diff --git a/livekit-rtc/tests/test_video.py b/livekit-rtc/tests/test_video.py new file mode 100644 index 00000000..857b84f8 --- /dev/null +++ b/livekit-rtc/tests/test_video.py @@ -0,0 +1,250 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""End-to-end video publish/subscribe tests.""" + +from __future__ import annotations + +import asyncio +import os +import struct +import zlib +from pathlib import Path + +import numpy as np + +from livekit import rtc + +from utils import create_token, skip_if_no_credentials, unique_room_name # type: ignore[import-not-found] + + +VIDEO_WIDTH = 640 +VIDEO_HEIGHT = 480 +VIDEO_FPS = 15 +VIDEO_COLOR_DURATION_SEC = 1.0 +# (name, RGB tuple) — order matters; the subscriber must see them in this sequence. +VIDEO_COLOR_SEQUENCE: list[tuple[str, tuple[int, int, int]]] = [ + ("red", (255, 0, 0)), + ("green", (0, 255, 0)), + ("blue", (0, 0, 255)), + ("white", (255, 255, 255)), + ("black", (0, 0, 0)), +] + + +def _solid_color_rgba_frame(width: int, height: int, rgb: tuple[int, int, int]) -> rtc.VideoFrame: + """Build a solid-color 640x480 RGBA `VideoFrame` for the given RGB triple.""" + pixels = np.empty((height, width, 4), dtype=np.uint8) + pixels[:, :, 0] = rgb[0] + pixels[:, :, 1] = rgb[1] + pixels[:, :, 2] = rgb[2] + pixels[:, :, 3] = 255 + return rtc.VideoFrame( + width=width, + height=height, + type=rtc.VideoBufferType.RGBA, + data=pixels.tobytes(), + ) + + +def _classify_frame_color( + frame_rgb: np.ndarray, + palette: list[tuple[str, tuple[int, int, int]]], +) -> tuple[str, float]: + """Return (nearest palette color name, euclidean distance) for the mean RGB of `frame_rgb`.""" + mean_rgb = frame_rgb[:, :, :3].reshape(-1, 3).mean(axis=0) + best_name, best_dist = palette[0][0], float("inf") + for name, rgb in palette: + dist = float(np.linalg.norm(mean_rgb - np.asarray(rgb, dtype=np.float64))) + if dist < best_dist: + best_name, best_dist = name, dist + return best_name, best_dist + + +def _save_rgba_frame_as_png(frame_rgba: np.ndarray, path: Path) -> None: + """Encode an (H, W, 4) uint8 RGBA array to a PNG file using only the stdlib.""" + height, width, _ = frame_rgba.shape + rgb = np.ascontiguousarray(frame_rgba[:, :, :3], dtype=np.uint8) + + def _chunk(tag: bytes, data: bytes) -> bytes: + return ( + struct.pack(">I", len(data)) + + tag + + data + + struct.pack(">I", zlib.crc32(tag + data) & 0xFFFFFFFF) + ) + + signature = b"\x89PNG\r\n\x1a\n" + # IHDR: width, height, bit depth=8, color type=2 (RGB), compression=0, filter=0, interlace=0 + ihdr = struct.pack(">IIBBBBB", width, height, 8, 2, 0, 0, 0) + # Each scanline must be prefixed with a filter byte (0 = None). + filter_col = np.zeros((height, 1), dtype=np.uint8) + scanlines = np.concatenate([filter_col, rgb.reshape(height, width * 3)], axis=1) + idat = zlib.compress(scanlines.tobytes(), level=6) + + with open(path, "wb") as f: + f.write(signature) + f.write(_chunk(b"IHDR", ihdr)) + f.write(_chunk(b"IDAT", idat)) + f.write(_chunk(b"IEND", b"")) + + +@skip_if_no_credentials() +class TestVideoStreamPublishSubscribe: + """End-to-end: publish a 640x480 color-cycle video and verify colors on the subscriber.""" + + async def test_video_stream_publish_subscribe(self) -> None: + """Publish red/green/blue/white/black (1s each, 15fps) and verify color sequence.""" + url = os.environ["LIVEKIT_URL"] + room_name = unique_room_name("test-video-colors") + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token("video-publisher", room_name) + subscriber_token = create_token("video-subscriber", room_name) + + track_subscribed_event = asyncio.Event() + subscribed_track: rtc.Track | None = None + + @subscriber_room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal subscribed_track + if track.kind == rtc.TrackKind.KIND_VIDEO: + subscribed_track = track + track_subscribed_event.set() + + try: + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + source = rtc.VideoSource(VIDEO_WIDTH, VIDEO_HEIGHT) + track = rtc.LocalVideoTrack.create_video_track("color-cycle", source) + options = rtc.TrackPublishOptions() + options.source = rtc.TrackSource.SOURCE_CAMERA + await publisher_room.local_participant.publish_track(track, options) + + await asyncio.wait_for(track_subscribed_event.wait(), timeout=10.0) + assert subscribed_track is not None + + # Request RGBA frames from the SFU so we don't have to convert per frame. + video_stream = rtc.VideoStream(subscribed_track, format=rtc.VideoBufferType.RGBA) + + received_frames: list[np.ndarray] = [] + stop_collecting = asyncio.Event() + + async def publish_colors() -> None: + await track_subscribed_event.wait() + frame_interval = 1.0 / VIDEO_FPS + frames_per_color = int(VIDEO_FPS * VIDEO_COLOR_DURATION_SEC) + loop = asyncio.get_event_loop() + start = loop.time() + global_idx = 0 + for _repeat in range(2): + for _color_name, rgb in VIDEO_COLOR_SEQUENCE: + frame = _solid_color_rgba_frame(VIDEO_WIDTH, VIDEO_HEIGHT, rgb) + for _ in range(frames_per_color): + source.capture_frame(frame) + global_idx += 1 + target = start + global_idx * frame_interval + sleep_for = target - loop.time() + if sleep_for > 0: + await asyncio.sleep(sleep_for) + + async def collect_frames() -> None: + async for event in video_stream: + vf = event.frame + if vf.type != rtc.VideoBufferType.RGBA: + vf = vf.convert(rtc.VideoBufferType.RGBA) + arr = ( + np.frombuffer(bytes(vf.data.cast("B")), dtype=np.uint8) + .reshape(vf.height, vf.width, 4) + .copy() + ) + received_frames.append(arr) + if stop_collecting.is_set(): + break + + collect_task = asyncio.create_task(collect_frames()) + publish_task = asyncio.create_task(publish_colors()) + + await publish_task + # Allow trailing frames to drain before signaling stop. + await asyncio.sleep(0.5) + stop_collecting.set() + + try: + await asyncio.wait_for(collect_task, timeout=3.0) + except asyncio.TimeoutError: + collect_task.cancel() + try: + await collect_task + except (asyncio.CancelledError, BaseException): + pass + + await video_stream.aclose() + await source.aclose() + + assert len(received_frames) > 0, "No video frames received" + + # Classify each received frame against the 5-color palette. + classified = [ + _classify_frame_color(f, VIDEO_COLOR_SEQUENCE)[0] for f in received_frames + ] + + # Reduce to stable runs: ignore single-frame transitions at color boundaries. + runs: list[tuple[str, int]] = [] + for color in classified: + if runs and runs[-1][0] == color: + runs[-1] = (color, runs[-1][1] + 1) + else: + runs.append((color, 1)) + # A stable run has several frames of the same classified color. + stable_run_min = max(3, VIDEO_FPS // 3) + dominant = {c for c, n in runs if n >= stable_run_min} + + expected_colors = {name for name, _ in VIDEO_COLOR_SEQUENCE} + missing_colors = expected_colors - dominant + assert not missing_colors, ( + f"Expected colors {expected_colors} not all present in subscribed track; " + f"missing {missing_colors}. Dominant colors: {dominant}. " + f"Full classified stream: {classified}" + ) + + # Snapshot the first received frame of each expected color to JPEG. + output_dir = Path(__file__).parent + saved: dict[str, Path] = {} + for idx, color_name in enumerate(classified): + if color_name in saved: + continue + if color_name not in (name for name, _ in VIDEO_COLOR_SEQUENCE): + continue + out_path = output_dir / f"subscriber_recv_frame_color_{color_name}.png" + _save_rgba_frame_as_png(received_frames[idx], out_path) + saved[color_name] = out_path + if len(saved) == len(VIDEO_COLOR_SEQUENCE): + break + + expected_names = {name for name, _ in VIDEO_COLOR_SEQUENCE} + missing = expected_names - saved.keys() + assert not missing, ( + f"Did not capture a frame for colors: {missing}. Classified stream: {classified}" + ) + finally: + await publisher_room.disconnect() + await subscriber_room.disconnect() diff --git a/livekit-rtc/tests/utils.py b/livekit-rtc/tests/utils.py new file mode 100644 index 00000000..a6b099ff --- /dev/null +++ b/livekit-rtc/tests/utils.py @@ -0,0 +1,239 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Shared helpers used across the livekit-rtc test suite.""" + +from __future__ import annotations + +import asyncio +import os +import uuid +from typing import Any, Callable, Optional, TypeVar + +import pytest + +from livekit import api, rtc +from livekit.rtc.room import EventTypes + +T = TypeVar("T") + +_REQUIRED_ENV_VARS = ("LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET") +DEFAULT_WAIT_TIMEOUT = 30.0 +DEFAULT_WAIT_INTERVAL = 0.1 + + +def skip_if_no_credentials() -> pytest.MarkDecorator: + """pytest mark that skips when any LiveKit credentials env var is missing.""" + missing = [var for var in _REQUIRED_ENV_VARS if not os.getenv(var)] + return pytest.mark.skipif( + bool(missing), reason=f"Missing environment variables: {', '.join(missing)}" + ) + + +def create_token(identity: str, room_name: str) -> str: + """Build a room-join JWT for the given identity/room.""" + return ( + api.AccessToken() + .with_identity(identity) + .with_name(identity) + .with_grants( + api.VideoGrants( + room_join=True, + room=room_name, + ) + ) + .to_jwt() + ) + + +def unique_room_name(base: str) -> str: + """Suffix the base with a short random token so concurrent runs don't collide.""" + return f"{base}-{uuid.uuid4().hex[:8]}" + + +async def assert_eventually( + condition: Callable[[], T], + timeout: float = 15.0, + interval: float = 0.1, + message: str = "Condition not met within timeout", +) -> T: + """Poll `condition()` until it returns a truthy value or `timeout` elapses.""" + deadline = asyncio.get_event_loop().time() + timeout + last_result: T | None = None + while asyncio.get_event_loop().time() < deadline: + last_result = condition() + if last_result: + return last_result + await asyncio.sleep(interval) + raise AssertionError(f"{message} (last result: {last_result})") + + +async def wait_until( + predicate: Callable[[], bool], + *, + timeout: float = DEFAULT_WAIT_TIMEOUT, + interval: float = DEFAULT_WAIT_INTERVAL, + message: str = "condition not met", +) -> None: + """Poll `predicate()` until it returns True or `timeout` elapses.""" + loop = asyncio.get_running_loop() + deadline = loop.time() + timeout + while loop.time() < deadline: + if predicate(): + return + await asyncio.sleep(interval) + raise AssertionError(f"timeout waiting: {message}") + + +async def ensure_rooms_all_connected( + rooms: list[rtc.Room], + *, + timeout: float = DEFAULT_WAIT_TIMEOUT, +) -> None: + """Wait until every `Room` in `rooms` reaches CONN_CONNECTED.""" + await wait_until( + lambda: all(r.connection_state == rtc.ConnectionState.CONN_CONNECTED for r in rooms), + timeout=timeout, + message="not all rooms reached CONN_CONNECTED", + ) + + +async def ensure_participants_visible( + observer: rtc.Room, + identities: list[str], + *, + timeout: float = DEFAULT_WAIT_TIMEOUT, +) -> None: + """Wait until `observer` sees every identity in `identities` as a remote participant.""" + + def _all_visible() -> bool: + seen = {p.identity for p in observer.remote_participants.values()} + return all(ident in seen for ident in identities) + + await wait_until( + _all_visible, + timeout=timeout, + message=( + f"not all identities visible to {observer.local_participant.identity}: {identities}" + ), + ) + + +def expect_room_event( + room: rtc.Room, + event: EventTypes, + predicate: Optional[Callable[..., bool]] = None, +) -> "asyncio.Future[tuple[Any, ...]]": + """Register a one-shot handler for `event` returning a `Future` resolved with the event args. + + `predicate` (if given) filters events; the handler is unregistered after the future resolves. + """ + loop = asyncio.get_running_loop() + fut: "asyncio.Future[tuple[Any, ...]]" = loop.create_future() + + def _on_event(*args: Any, **kwargs: Any) -> None: + if fut.done(): + return + if predicate is None or predicate(*args, **kwargs): + fut.set_result(args) + room.off(event, _on_event) + + room.on(event, _on_event) + return fut + + +async def await_event( + fut: "asyncio.Future[Any]", + timeout: float = DEFAULT_WAIT_TIMEOUT, +) -> None: + """Await a future from `expect_room_event` with a descriptive timeout failure.""" + try: + await asyncio.wait_for(fut, timeout=timeout) + except asyncio.TimeoutError as e: + raise AssertionError("timed out waiting for event") from e + + +async def connect_room( + identity: str, + room_name: str, + *, + room: Optional[rtc.Room] = None, + options: Optional[rtc.RoomOptions] = None, +) -> rtc.Room: + """Build a token for `identity`/`room_name` and connect. + + If `room` is provided it is connected in place; otherwise a fresh `rtc.Room` + is created. Returns the connected room. + """ + if room is None: + room = rtc.Room() + url = os.environ["LIVEKIT_URL"] + token = create_token(identity, room_name) + if options is None: + await room.connect(url, token) + else: + await room.connect(url, token, options=options) + return room + + +async def ensure_track_subscribed( + room: rtc.Room, + track_sid: str, + *, + timeout: float = DEFAULT_WAIT_TIMEOUT, +) -> rtc.RemoteTrackPublication: + """Wait until some remote participant in `room` has subscribed to `track_sid`.""" + holder: dict[str, rtc.RemoteTrackPublication] = {} + + def _has_subscribed() -> bool: + for participant in room.remote_participants.values(): + pub = participant.track_publications.get(track_sid) + if pub is not None and pub.subscribed and pub.track is not None: + holder["pub"] = pub + return True + return False + + await wait_until( + _has_subscribed, + timeout=timeout, + message=f"room did not subscribe to track {track_sid}", + ) + return holder["pub"] + + +async def publish_dummy_video( + source: rtc.VideoSource, + stop_event: asyncio.Event, + *, + width: int = 320, + height: int = 180, + fps: int = 15, +) -> None: + """Continuously capture ARGB frames into `source` until `stop_event` is set. + + Pixel values vary frame-to-frame so encryption tests see distinct ciphertexts. + """ + pixel_count = width * height + frame_idx = 0 + while not stop_event.is_set(): + fill = frame_idx % 256 + pixel = bytes((255, fill, (fill + 85) % 256, (fill + 170) % 256)) + buf = pixel * pixel_count + frame = rtc.VideoFrame(width, height, rtc.VideoBufferType.ARGB, buf) + source.capture_frame(frame) + frame_idx += 1 + try: + await asyncio.wait_for(stop_event.wait(), timeout=1.0 / fps) + except asyncio.TimeoutError: + pass diff --git a/makefile b/makefile new file mode 100644 index 00000000..5b9472c6 --- /dev/null +++ b/makefile @@ -0,0 +1,310 @@ +.PHONY: help bootstrap install format format-check lint lint-fix check type-check clean build \ + build-rtc build-wheel generate-proto download-ffi status doctor + +# Colors for output +CYAN := \033[36m +GREEN := \033[32m +YELLOW := \033[33m +RED := \033[31m +RESET := \033[0m +BOLD := \033[1m + +# Paths (computed as absolute paths) +MAKEFILE_DIR := $(shell pwd) +PYTHON_RTC := $(MAKEFILE_DIR)/livekit-rtc +PYTHON_API := $(MAKEFILE_DIR)/livekit-api +PYTHON_PROTOCOL := $(MAKEFILE_DIR)/livekit-protocol +RUST_SUBMODULE := $(MAKEFILE_DIR)/livekit-rtc/rust-sdks + +# Platform and architecture auto-detection +ARCH := $(shell uname -m) +OS := $(shell uname -s | tr A-Z a-z) + +# Default target +.DEFAULT_GOAL := help + +help: ## Show this help message + @echo "$(BOLD)$(CYAN)Available targets:$(RESET)" + @echo "" + @echo "$(BOLD)Development Workflows:$(RESET)" + @grep -E '^(bootstrap|build-rtc|build-wheel|download-ffi|status|doctor):.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-20s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$(BOLD)Code Quality:$(RESET)" + @grep -E '^(format|format-check|lint|lint-fix|type-check|check):.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-20s$(RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$(BOLD)Other:$(RESET)" + @grep -E '^(install|clean|build):.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(CYAN)%-20s$(RESET) %s\n", $$1, $$2}' + +install: ## Install all dependencies with dev extras + @echo "$(BOLD)$(CYAN)Installing dependencies...$(RESET)" + @uv sync --all-extras --dev + @echo "$(BOLD)$(GREEN)✓ Dependencies installed$(RESET)" + +format: ## Format code with ruff + @echo "$(BOLD)$(CYAN)Formatting code...$(RESET)" + @uv run ruff format . + @echo "$(BOLD)$(GREEN)✓ Code formatted$(RESET)" + +format-check: ## Check code formatting without making changes + @echo "$(BOLD)$(CYAN)Checking code formatting...$(RESET)" + @if uv run ruff format --check .; then \ + echo "$(BOLD)$(GREEN)✓ Code formatting is correct$(RESET)"; \ + else \ + echo "$(BOLD)$(RED)✗ Code formatting issues found. Run 'make format' to fix.$(RESET)"; \ + exit 1; \ + fi + +lint: ## Run ruff linter + @echo "$(BOLD)$(CYAN)Running linter...$(RESET)" + @if uv run ruff check .; then \ + echo "$(BOLD)$(GREEN)✓ No linting issues found$(RESET)"; \ + else \ + echo "$(BOLD)$(RED)✗ Linting issues found$(RESET)"; \ + exit 1; \ + fi + +lint-fix: ## Run ruff linter and fix issues automatically + @echo "$(BOLD)$(CYAN)Running linter with auto-fix...$(RESET)" + @uv run ruff check --fix . + @echo "$(BOLD)$(GREEN)✓ Linting complete$(RESET)" + +type-check: ## Run mypy type checker + @echo "$(BOLD)$(CYAN)Running type checker...$(RESET)" + @if uv run mypy livekit-protocol livekit-api livekit-rtc; then \ + echo "$(BOLD)$(GREEN)✓ Type checking passed$(RESET)"; \ + else \ + echo "$(BOLD)$(RED)✗ Type checking failed$(RESET)"; \ + exit 1; \ + fi + +check: format-check lint type-check ## Run all checks (format, lint, type-check) + @echo "$(BOLD)$(GREEN)✓ All checks passed!$(RESET)" + +# ============================================ +# Development Workflows +# ============================================ + +download-ffi: ## Download pre-built FFI artifacts for livekit-rtc + @echo "$(BOLD)$(CYAN)📦 Downloading FFI artifacts...$(RESET)" + @set -e; \ + DETECTED_ARCH="$(ARCH)"; \ + DETECTED_OS="$(OS)"; \ + if [ "$$DETECTED_ARCH" = "aarch64" ]; then \ + PLATFORM_ARCH="arm64"; \ + else \ + PLATFORM_ARCH="$$DETECTED_ARCH"; \ + fi; \ + if [ "$$DETECTED_OS" = "darwin" ]; then \ + PLATFORM_OS="macos"; \ + else \ + PLATFORM_OS="$$DETECTED_OS"; \ + fi; \ + echo "$(CYAN) Platform: $$PLATFORM_OS-$$PLATFORM_ARCH$(RESET)"; \ + cd $(PYTHON_RTC) && python rust-sdks/download_ffi.py --platform "$$PLATFORM_OS" --arch "$$PLATFORM_ARCH" --output livekit/rtc/resources; \ + echo "$(BOLD)$(GREEN)✅ FFI artifacts downloaded$(RESET)" + +build-rtc: ## Build livekit-ffi from local rust-sdks and generate proto + @echo "$(BOLD)$(CYAN)🦀 Building livekit-ffi from source...$(RESET)" + @set -e; \ + if [ ! -d "$(RUST_SUBMODULE)" ]; then \ + echo "$(BOLD)$(RED)✗ Error: rust-sdks submodule not found at $(RUST_SUBMODULE)$(RESET)"; \ + exit 1; \ + fi; \ + echo "$(CYAN)🦀 Building livekit-ffi...$(RESET)"; \ + cd $(RUST_SUBMODULE) && cargo build --release -p livekit-ffi; \ + echo "$(CYAN)📝 Generating protobuf FFI protocol...$(RESET)"; \ + cd $(PYTHON_RTC) && ./generate_proto.sh; \ + RUST_LIB_DIR="$$(cd $(RUST_SUBMODULE) && pwd)/target/release"; \ + if [ "$(OS)" = "darwin" ]; then \ + RUST_LIB_PATH="$$RUST_LIB_DIR/liblivekit_ffi.dylib"; \ + elif [ "$(OS)" = "linux" ]; then \ + RUST_LIB_PATH="$$RUST_LIB_DIR/liblivekit_ffi.so"; \ + else \ + RUST_LIB_PATH="$$RUST_LIB_DIR/livekit_ffi.dll"; \ + fi; \ + echo "$(BOLD)$(GREEN)✅ Built livekit-ffi from source$(RESET)"; \ + echo ""; \ + echo "$(BOLD)$(YELLOW)📋 To use the local rust lib, export the following:$(RESET)"; \ + echo "$(BOLD) export LIVEKIT_LIB_PATH=$$RUST_LIB_PATH$(RESET)" + +build-wheel: ## Build wheel for a package (usage: make build-wheel PACKAGE=livekit-rtc) + @echo "$(BOLD)$(CYAN)📦 Building wheel...$(RESET)" + @set -e; \ + if [ -z "$(PACKAGE)" ]; then \ + echo "$(BOLD)$(RED)✗ Error: PACKAGE parameter is required$(RESET)"; \ + echo "$(YELLOW)Usage: make build-wheel PACKAGE=$(RESET)"; \ + echo "$(YELLOW)Available packages: livekit-rtc, livekit-api, livekit-protocol$(RESET)"; \ + exit 1; \ + fi; \ + PACKAGE_PATH="$(MAKEFILE_DIR)/$(PACKAGE)"; \ + if [ ! -d "$$PACKAGE_PATH" ]; then \ + echo "$(BOLD)$(RED)✗ Error: Package directory not found: $$PACKAGE_PATH$(RESET)"; \ + exit 1; \ + fi; \ + if [ ! -f "$$PACKAGE_PATH/pyproject.toml" ]; then \ + echo "$(BOLD)$(RED)✗ Error: pyproject.toml not found in $$PACKAGE_PATH$(RESET)"; \ + exit 1; \ + fi; \ + echo "$(CYAN) Package: $(PACKAGE)$(RESET)"; \ + echo "$(CYAN) Building in: $$PACKAGE_PATH$(RESET)"; \ + cd "$$PACKAGE_PATH" && uv build --out-dir ./dist; \ + echo "$(BOLD)$(GREEN)✅ Wheel built successfully$(RESET)"; \ + echo "$(CYAN) Output: $$PACKAGE_PATH/dist/$(RESET)" + +status: ## Show current development environment status + @echo "$(BOLD)$(CYAN)📍 Current status:$(RESET)" + @echo "" + @set -e; \ + echo "$(BOLD)📦 Packages:$(RESET)"; \ + for pkg in livekit livekit-api livekit-protocol; do \ + SHOW_OUTPUT=$$(uv pip show $$pkg 2>/dev/null || echo ""); \ + if [ -z "$$SHOW_OUTPUT" ]; then \ + echo " $$pkg: NOT INSTALLED"; \ + elif echo "$$SHOW_OUTPUT" | grep -q "Editable project location:"; then \ + VERSION=$$(echo "$$SHOW_OUTPUT" | grep "^Version:" | awk '{print $$2}'); \ + LOCATION=$$(echo "$$SHOW_OUTPUT" | grep "Editable project location:" | cut -d' ' -f4-); \ + echo " $$pkg: LOCAL (editable) v$$VERSION"; \ + echo " path: $$LOCATION"; \ + else \ + VERSION=$$(echo "$$SHOW_OUTPUT" | grep "^Version:" | awk '{print $$2}'); \ + echo " $$pkg: PyPI v$$VERSION"; \ + fi; \ + done; \ + echo ""; \ + echo "$(BOLD)🦀 FFI Status:$(RESET)"; \ + if [ -n "$$LIVEKIT_LIB_PATH" ]; then \ + if [ -f "$$LIVEKIT_LIB_PATH" ]; then \ + echo " FFI: CUSTOM (from LIVEKIT_LIB_PATH env var)"; \ + echo " path: $$LIVEKIT_LIB_PATH"; \ + else \ + echo " FFI: CUSTOM (LIVEKIT_LIB_PATH set but file not found)"; \ + echo " path: $$LIVEKIT_LIB_PATH"; \ + fi; \ + else \ + FFI_PATH="$(PYTHON_RTC)/livekit/rtc/resources"; \ + if [ -d "$$FFI_PATH" ] && { [ -f "$$FFI_PATH/liblivekit_ffi.dylib" ] || [ -f "$$FFI_PATH/liblivekit_ffi.so" ] || [ -f "$$FFI_PATH/livekit_ffi.dll" ]; }; then \ + RUST_SUBMODULE_DIR="$$(cd $(RUST_SUBMODULE) 2>/dev/null && pwd || echo "")"; \ + CARGO_TOML_PATH="$$RUST_SUBMODULE_DIR/livekit-ffi/Cargo.toml"; \ + if [ -f "$$CARGO_TOML_PATH" ]; then \ + FFI_VERSION=$$(grep '^version = ' "$$CARGO_TOML_PATH" | head -1 | sed 's/.*"\(.*\)".*/\1/'); \ + echo " FFI: PRE-BUILT ARTIFACTS (v$$FFI_VERSION)"; \ + else \ + echo " FFI: PRE-BUILT ARTIFACTS"; \ + fi; \ + else \ + echo " FFI: NOT AVAILABLE (run 'make download-ffi')"; \ + fi; \ + fi + +doctor: ## Check development environment health + @echo "$(BOLD)$(CYAN)🏥 Running diagnostics...$(RESET)" + @echo "" + @ISSUES=0; \ + echo "$(BOLD)📦 Required Tools:$(RESET)"; \ + if command -v uv &> /dev/null; then \ + UV_VERSION=$$(uv --version 2>&1 | head -1); \ + echo " ✓ uv: $$UV_VERSION"; \ + else \ + echo " ✗ uv: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + if command -v python &> /dev/null; then \ + PYTHON_VERSION=$$(python --version 2>&1); \ + echo " ✓ python: $$PYTHON_VERSION"; \ + else \ + echo " ✗ python: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + if command -v cargo &> /dev/null; then \ + CARGO_VERSION=$$(cargo --version 2>&1); \ + echo " ✓ cargo: $$CARGO_VERSION"; \ + else \ + echo " ⚠ cargo: NOT FOUND (required for 'make build-rtc')"; \ + fi; \ + if command -v git &> /dev/null; then \ + GIT_VERSION=$$(git --version 2>&1); \ + echo " ✓ git: $$GIT_VERSION"; \ + else \ + echo " ✗ git: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + echo ""; \ + echo "$(BOLD)📂 Repository Structure:$(RESET)"; \ + REPO_ROOT=$$(cd $(MAKEFILE_DIR) && git rev-parse --show-toplevel 2>/dev/null || echo ""); \ + if [ -n "$$REPO_ROOT" ]; then \ + BRANCH=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \ + COMMIT=$$(git rev-parse --short HEAD 2>/dev/null || echo "unknown"); \ + echo " ✓ python-sdks: $$REPO_ROOT"; \ + echo " Branch: $$BRANCH @ $$COMMIT"; \ + fi; \ + if [ -d "$(PYTHON_RTC)" ]; then \ + echo " ✓ livekit-rtc: $(PYTHON_RTC)"; \ + else \ + echo " ✗ livekit-rtc: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + if [ -d "$(PYTHON_API)" ]; then \ + echo " ✓ livekit-api: $(PYTHON_API)"; \ + else \ + echo " ✗ livekit-api: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + if [ -d "$(PYTHON_PROTOCOL)" ]; then \ + echo " ✓ livekit-protocol: $(PYTHON_PROTOCOL)"; \ + else \ + echo " ✗ livekit-protocol: NOT FOUND"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + if [ -d "$(RUST_SUBMODULE)" ]; then \ + echo " ✓ rust-sdks: $(RUST_SUBMODULE)"; \ + if [ -e "$(RUST_SUBMODULE)/.git" ]; then \ + RUST_BRANCH=$$(cd $(RUST_SUBMODULE) && git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \ + RUST_COMMIT=$$(cd $(RUST_SUBMODULE) && git rev-parse --short HEAD 2>/dev/null || echo "unknown"); \ + echo " Branch: $$RUST_BRANCH @ $$RUST_COMMIT"; \ + fi; \ + else \ + echo " ⚠ rust-sdks: NOT FOUND (needed for 'make build-rtc')"; \ + fi; \ + echo ""; \ + echo "$(BOLD)🔍 Current Configuration:$(RESET)"; \ + if [ -d ".venv" ]; then \ + echo " ✓ Virtual environment: .venv exists"; \ + else \ + echo " ⚠ Virtual environment: .venv not found (run 'make install')"; \ + fi; \ + for pkg in livekit livekit-api livekit-protocol; do \ + SHOW_OUTPUT=$$(uv pip show $$pkg 2>/dev/null || echo ""); \ + if [ -z "$$SHOW_OUTPUT" ]; then \ + echo " ✗ $$pkg: NOT INSTALLED"; \ + elif echo "$$SHOW_OUTPUT" | grep -q "Editable project location:"; then \ + VERSION=$$(echo "$$SHOW_OUTPUT" | grep "^Version:" | awk '{print $$2}'); \ + echo " ✓ $$pkg: LOCAL (editable) v$$VERSION"; \ + else \ + VERSION=$$(echo "$$SHOW_OUTPUT" | grep "^Version:" | awk '{print $$2}'); \ + echo " ✓ $$pkg: PyPI v$$VERSION"; \ + fi; \ + done; \ + if [ -n "$$LIVEKIT_LIB_PATH" ]; then \ + if [ -f "$$LIVEKIT_LIB_PATH" ]; then \ + echo " ✓ FFI: Custom build from LIVEKIT_LIB_PATH"; \ + else \ + echo " ✗ FFI: LIVEKIT_LIB_PATH set but file not found: $$LIVEKIT_LIB_PATH"; \ + ISSUES=$$((ISSUES + 1)); \ + fi; \ + fi; \ + echo ""; \ + if [ $$ISSUES -eq 0 ]; then \ + echo "$(BOLD)$(GREEN)✅ All checks passed! Environment is healthy.$(RESET)"; \ + else \ + echo "$(BOLD)$(RED)⚠️ Found $$ISSUES issue(s). Please fix the errors above.$(RESET)"; \ + exit 1; \ + fi + +bootstrap: ## Sync repo, deps, and assets to a working dev state (safe to re-run) + @echo "$(BOLD)$(CYAN)🔄 Syncing development environment...$(RESET)" + @git submodule update --init --recursive + @git lfs install + @git lfs pull + @$(MAKE) install + @$(MAKE) download-ffi + @echo "$(BOLD)$(GREEN)✓ Sync complete$(RESET)" \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e3dcfc97 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,77 @@ +[project] +name = "python-sdks" +version = "0.1.0" +description = "LiveKit Python SDKs monorepo" +requires-python = ">=3.9" +dependencies = ["livekit", "livekit-api", "livekit-protocol"] + +[tool.uv.workspace] +members = ["livekit-rtc", "livekit-api", "livekit-protocol"] + +[tool.uv.sources] +livekit = { workspace = true } +livekit-api = { workspace = true } +livekit-protocol = { workspace = true } + +[dependency-groups] +dev = [ + # Linting and type checking + "ruff>=0.8.5", + "mypy>=1.13.0", + "types-aiofiles>=24", + "ipython>=8.0.0", + # Testing + "pytest>=8.3.4", + "pytest-asyncio>=0.24.0", + # Build and packaging + "requests", + "wheel", + "twine", + "auditwheel; sys_platform == 'linux'", + "cibuildwheel", + # Additional dependencies + "matplotlib", + "pydantic", + "numpy", +] + +[tool.ruff] +line-length = 100 +target-version = "py39" +exclude = [".github"] + +[tool.ruff.lint] +select = [ + "E", # pycodestyle errors + "W", # pycodestyle warnings + "F", # pyflakes + "I", # isort + "B", # flake8-bugbear + "C4", # flake8-comprehensions + "UP", # pyupgrade +] +ignore = ["E501"] + +[tool.ruff.lint.isort] +combine-as-imports = true +known-first-party = ["livekit"] + +[tool.ruff.lint.pydocstyle] +convention = "google" + + +[tool.pytest.ini_options] +testpaths = ["tests"] +asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "function" +addopts = ["--import-mode=importlib", "--ignore=examples"] + + +[tool.mypy] +exclude = "(build|setup\\.py|livekit-rtc/rust-sdks.*)" +namespace_packages = true +explicit_package_bases = true +mypy_path = "livekit-protocol:livekit-api:livekit-rtc" +strict = true +disallow_any_generics = false +plugins = ["pydantic.mypy"] diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..104450ec --- /dev/null +++ b/renovate.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:base", "helpers:pinGitHubActionDigests"], + "minimumReleaseAge": "2 weeks", + "commitBody": "Generated by renovateBot", + "packageRules": [ + { + "matchManagers": ["github-actions"], + "groupName": "github workflows" + }, + { + "matchManagers": ["pip_requirements", "pip_setup", "pep621"], + "groupName": "python deps", + "schedule": ["before 9am on monday"] + }, + { + "matchManagers": ["pep621"], + "matchDepTypes": ["dev", "dependency-groups"], + "matchPackageNames": ["!/^livekit/"], + "matchUpdateTypes": ["minor", "patch", "pin", "digest"], + "groupName": "python dev deps (non-major)", + "schedule": ["before 9am on monday"] + }, + { + "matchFileNames": ["livekit-rtc/jupyter-html/package.json"], + "matchPackageNames": ["livekit-client", "/^@livekit//"], + "groupName": "jupyter-html livekit deps" + }, + { + "matchFileNames": ["livekit-rtc/jupyter-html/package.json"], + "matchPackageNames": ["!livekit-client", "!/^@livekit//"], + "groupName": "jupyter-html deps" + } + ] +} diff --git a/ruff.toml b/ruff.toml index c1dc76a4..8d44a7dc 100644 --- a/ruff.toml +++ b/ruff.toml @@ -3,7 +3,7 @@ exclude = [ "livekit-protocol/livekit/protocol" ] -line-length = 88 +line-length = 100 indent-width = 4 target-version = "py39" diff --git a/tests/api/test_access_token.py b/tests/api/test_access_token.py new file mode 100644 index 00000000..85aeb6ce --- /dev/null +++ b/tests/api/test_access_token.py @@ -0,0 +1,103 @@ +import datetime + +import pytest +from livekit.api import AccessToken, TokenVerifier, VideoGrants, SIPGrants +from livekit.protocol.room import RoomConfiguration +from livekit.protocol.agent_dispatch import RoomAgentDispatch + +TEST_API_KEY = "myapikey" +TEST_API_SECRET = "thiskeyistotallyunsafe" + + +def test_verify_token() -> None: + grants = VideoGrants(room_join=True, room="test_room") + sip = SIPGrants(admin=True) + + token = ( + AccessToken(TEST_API_KEY, TEST_API_SECRET) + .with_identity("test_identity") + .with_metadata("test_metadata") + .with_grants(grants) + .with_sip_grants(sip) + .with_attributes({"key1": "value1", "key2": "value2"}) + .to_jwt() + ) + + token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) + claims = token_verifier.verify(token) + + assert claims.identity == "test_identity" + assert claims.metadata == "test_metadata" + assert claims.video == grants + assert claims.sip == sip + assert claims.attributes is not None + assert claims.attributes["key1"] == "value1" + assert claims.attributes["key2"] == "value2" + + +def test_agent_config() -> None: + token = ( + AccessToken(TEST_API_KEY, TEST_API_SECRET) + .with_identity("test_identity") + .with_grants(VideoGrants(room_join=True, room="test_room")) + .with_room_config( + RoomConfiguration( + agents=[RoomAgentDispatch(agent_name="test-agent")], + ), + ) + .to_jwt() + ) + + token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) + claims = token_verifier.verify(token) + # Verify the decoded claims match + assert claims.room_config is not None + assert claims.room_config.agents[0].agent_name == "test-agent" + + # Split token into header.payload.signature + parts = token.split(".") + import base64 + import json + + # Decode the payload (middle part) + payload = parts[1] + # Add padding if needed + padding = len(payload) % 4 + if padding: + payload += "=" * (4 - padding) + decoded = base64.b64decode(payload) + payload_json = json.loads(decoded) + print(decoded) + + # Verify the room_config and agents were encoded correctly + assert "roomConfig" in payload_json + assert "agents" in payload_json["roomConfig"] + assert len(payload_json["roomConfig"]["agents"]) == 1 + assert payload_json["roomConfig"]["agents"][0]["agentName"] == "test-agent" + + +def test_verify_token_invalid() -> None: + token = AccessToken(TEST_API_KEY, TEST_API_SECRET).with_identity("test_identity").to_jwt() + + token_verifier = TokenVerifier(TEST_API_KEY, "invalid_secret") + with pytest.raises(Exception): + token_verifier.verify(token) + + token_verifier = TokenVerifier("invalid_key", TEST_API_SECRET) + with pytest.raises(Exception): + token_verifier.verify(token) + + +def test_verify_token_expired() -> None: + token = ( + AccessToken(TEST_API_KEY, TEST_API_SECRET) + .with_identity("test_identity") + .with_ttl(datetime.timedelta(seconds=-1)) + .to_jwt() + ) + + token_verifier = TokenVerifier( + TEST_API_KEY, TEST_API_SECRET, leeway=datetime.timedelta(seconds=0) + ) + with pytest.raises(Exception): + token_verifier.verify(token) diff --git a/tests/api/test_failover.py b/tests/api/test_failover.py new file mode 100644 index 00000000..f9521c44 --- /dev/null +++ b/tests/api/test_failover.py @@ -0,0 +1,115 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Region failover tests against the shared mock LiveKit API server +(livekit/livekit cmd/test-server). Point them at a running instance with +LK_TEST_SERVER_URL (default http://127.0.0.1:9999); they skip when no server is +reachable. The mock returns Cache-Control: max-age=0, so the region cache never +stores entries and scenarios don't interfere. + +See cmd/test-server/README.md for the X-Lk-Mock JSON control protocol. These +tests drive TwirpClient.request() directly because failover relies on internal +test-only knobs (_failover_force/_failover_backoff) the public methods don't +expose. +""" + +import asyncio +import json +import os +import urllib.request + +import aiohttp +import pytest + +from livekit.api import CreateRoomRequest, Room, ServerError +from livekit.api.twirp_client import TwirpClient + +BASE = os.getenv("LK_TEST_SERVER_URL", "http://127.0.0.1:9999") + + +def _server_up() -> bool: + try: + with urllib.request.urlopen(f"{BASE}/settings/regions", timeout=1) as r: + return r.status == 200 + except Exception: + return False + + +pytestmark = pytest.mark.skipif( + not _server_up(), reason=f"mock test server not reachable at {BASE}" +) + + +# _failover_force bypasses the cloud-host check (the mock is on 127.0.0.1) and a +# tiny backoff keeps the tests fast — both are internal, test-only knobs. +async def _call(mock: dict, *, failover: bool = True, force: bool = True) -> Room: + async with aiohttp.ClientSession() as session: + client = TwirpClient( + session, + BASE, + "livekit", + failover=failover, + _failover_force=force, + _failover_backoff=0.001, + ) + headers = { + "authorization": "Bearer test-token", + # These tests exercise failover, not authz; skip the mock's permission check. + "X-Lk-Mock": json.dumps({"skipAuth": True, **mock}), + } + return await client.request("RoomService", "CreateRoom", CreateRoomRequest(), headers, Room) + + +def test_healthy(): + asyncio.run(_call({})) + + +def test_primary_unavailable(): + asyncio.run(_call({"failRegions": [0]})) + + +def test_two_regions_unavailable(): + asyncio.run(_call({"failRegions": [0, 1]})) + + +def test_all_unavailable(): + with pytest.raises(ServerError): + asyncio.run(_call({"failRegions": [0, 1, 2, 3]})) + + +def test_client_error_not_retried(): + with pytest.raises(ServerError) as exc: + asyncio.run(_call({"failRegions": [0], "failStatus": 400})) + assert exc.value.code == "invalid_argument" + + +def test_transport_error_failover(): + asyncio.run(_call({"failRegions": [0], "failMode": "drop"})) + + +def test_region_discovery_unreachable(): + with pytest.raises(ServerError): + asyncio.run(_call({"failRegions": [0], "regionsStatus": 500})) + + +def test_not_cloud_host(): + # Enabled but not forced; 127.0.0.1 is not a cloud host, so no failover. + with pytest.raises(ServerError): + asyncio.run(_call({"failRegions": [0]}, force=False)) + + +def test_disabled(): + # failover=False disables failover entirely. + with pytest.raises(ServerError): + asyncio.run(_call({"failRegions": [0]}, failover=False)) diff --git a/tests/api/test_livekitapi.py b/tests/api/test_livekitapi.py new file mode 100644 index 00000000..f84bfa48 --- /dev/null +++ b/tests/api/test_livekitapi.py @@ -0,0 +1,586 @@ +# Copyright 2026 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""API tests that drive the unified LiveKitAPI against the mock LiveKit server +(livekit/livekit cmd/test-server). Point them at a running instance with +LK_TEST_SERVER_URL (default http://127.0.0.1:9999); they skip when no server is +reachable. + +Because the mock enforces the same per-method grants as the real server, a call +that succeeds also proves the SDK attached the right grants automatically. The +smoke tests fully populate each request so they double as a reference for a +complete call and exercise field serialization. Mock directives are passed via +the X-Lk-Mock header, set as a default header on the session (the public service +methods don't expose per-call headers). +""" + +import asyncio +import contextlib +import json +import os +import urllib.request +from typing import Optional + +import aiohttp +import pytest +from google.protobuf.duration_pb2 import Duration + +import livekit.api as api +from livekit.api import SipCallError, ServerError +from livekit.protocol.rtc import SessionDescription + +BASE = os.getenv("LK_TEST_SERVER_URL", "http://127.0.0.1:9999") +# devkey/secret match `livekit-server --dev`, which the mock verifies against. +KEY, SECRET = "devkey", "secret" + + +def _server_up() -> bool: + try: + with urllib.request.urlopen(f"{BASE}/settings/regions", timeout=1) as r: + return r.status == 200 + except Exception: + return False + + +pytestmark = pytest.mark.skipif( + not _server_up(), reason=f"mock test server not reachable at {BASE}" +) + + +@contextlib.asynccontextmanager +async def _api(mock: Optional[dict] = None): + """A LiveKitAPI whose session carries the given X-Lk-Mock directives (if any) + as a default header on every request.""" + headers = {"X-Lk-Mock": json.dumps(mock)} if mock else None + async with aiohttp.ClientSession(headers=headers) as session: + yield api.LiveKitAPI(BASE, KEY, SECRET, session=session) + + +def _mp4(path: str) -> api.EncodedFileOutput: + return api.EncodedFileOutput(file_type=api.EncodedFileType.MP4, filepath=path) + + +# -- smoke: fully-populated calls across every service ------------------------ + + +async def _room_smoke(): + async with _api() as lk: + await lk.room.create_room( + api.CreateRoomRequest( + name="test-room", + empty_timeout=300, + departure_timeout=60, + max_participants=50, + metadata='{"scene":"lobby"}', + min_playout_delay=100, + max_playout_delay=2000, + sync_streams=True, + agents=[api.RoomAgentDispatch(agent_name="greeter", metadata='{"lang":"en"}')], + ) + ) + await lk.room.list_rooms(api.ListRoomsRequest(names=["test-room", "lobby"])) + await lk.room.delete_room(api.DeleteRoomRequest(room="test-room")) + await lk.room.list_participants(api.ListParticipantsRequest(room="test-room")) + await lk.room.get_participant( + api.RoomParticipantIdentity(room="test-room", identity="participant-42") + ) + await lk.room.remove_participant( + api.RoomParticipantIdentity(room="test-room", identity="participant-42") + ) + await lk.room.forward_participant( + api.ForwardParticipantRequest( + room="test-room", identity="participant-42", destination_room="overflow-room" + ) + ) + await lk.room.move_participant( + api.MoveParticipantRequest( + room="test-room", identity="participant-42", destination_room="breakout-room" + ) + ) + await lk.room.mute_published_track( + api.MuteRoomTrackRequest( + room="test-room", identity="participant-42", track_sid="TR_video1", muted=True + ) + ) + await lk.room.update_participant( + api.UpdateParticipantRequest( + room="test-room", + identity="participant-42", + name="Alice", + metadata='{"role":"host"}', + attributes={"seat": "1A"}, + permission=api.ParticipantPermission( + can_subscribe=True, + can_publish=True, + can_publish_data=True, + can_publish_sources=[api.TrackSource.MICROPHONE, api.TrackSource.CAMERA], + can_update_metadata=True, + ), + ) + ) + await lk.room.update_subscriptions( + api.UpdateSubscriptionsRequest( + room="test-room", + identity="participant-42", + track_sids=["TR_video1"], + subscribe=True, + participant_tracks=[ + api.ParticipantTracks(participant_sid="PA_xyz789", track_sids=["TR_video1"]) + ], + ) + ) + await lk.room.update_room_metadata( + api.UpdateRoomMetadataRequest(room="test-room", metadata='{"scene":"intro"}') + ) + await lk.room.send_data( + api.SendDataRequest( + room="test-room", + data=b"hello world", + kind=api.DataPacket.RELIABLE, + destination_identities=["participant-42"], + topic="chat", + ) + ) + + +async def _egress_smoke(): + async with _api() as lk: + await lk.egress.start_room_composite_egress( + api.RoomCompositeEgressRequest( + room_name="test-room", layout="grid", file_outputs=[_mp4("room.mp4")] + ) + ) + await lk.egress.start_web_egress( + api.WebEgressRequest( + url="https://example.com/scene", + stream_outputs=[ + api.StreamOutput( + protocol=api.StreamProtocol.RTMP, urls=["rtmps://a.example.com/live/key"] + ) + ], + ) + ) + await lk.egress.start_participant_egress( + api.ParticipantEgressRequest( + room_name="test-room", + identity="participant-42", + screen_share=True, + file_outputs=[_mp4("participant.mp4")], + ) + ) + await lk.egress.start_track_composite_egress( + api.TrackCompositeEgressRequest( + room_name="test-room", + audio_track_id="TR_audio1", + video_track_id="TR_video1", + file_outputs=[_mp4("track-composite.mp4")], + ) + ) + await lk.egress.start_track_egress( + api.TrackEgressRequest( + room_name="test-room", + track_id="TR_video1", + file=api.DirectFileOutput(filepath="track.mp4"), + ) + ) + await lk.egress.start_egress( + api.StartEgressRequest( + room_name="test-room", + web=api.WebSource(url="https://example.com/scene"), + outputs=[ + api.Output( + file=api.FileOutput( + file_type=api.EncodedFileType.MP4, filepath="unified.mp4" + ) + ) + ], + ) + ) + await lk.egress.update_layout( + api.UpdateLayoutRequest(egress_id="EG_abc123", layout="speaker") + ) + await lk.egress.update_stream( + api.UpdateStreamRequest( + egress_id="EG_abc123", + add_output_urls=["rtmps://b.example.com/live/key"], + remove_output_urls=["rtmps://a.example.com/live/key"], + ) + ) + await lk.egress.list_egress( + api.ListEgressRequest(room_name="test-room", egress_id="EG_abc123", active=True) + ) + await lk.egress.stop_egress(api.StopEgressRequest(egress_id="EG_abc123")) + + +async def _ingress_smoke(): + async with _api() as lk: + await lk.ingress.create_ingress( + api.CreateIngressRequest( + input_type=api.IngressInput.RTMP_INPUT, + name="stream-input", + room_name="test-room", + participant_identity="ingress-bot", + participant_name="Live Stream", + participant_metadata='{"source":"rtmp"}', + enable_transcoding=True, + audio=api.IngressAudioOptions( + name="audio", + source=api.TrackSource.MICROPHONE, + preset=api.IngressAudioEncodingPreset.OPUS_STEREO_96KBPS, + ), + video=api.IngressVideoOptions( + name="video", + source=api.TrackSource.CAMERA, + preset=api.IngressVideoEncodingPreset.H264_1080P_30FPS_3_LAYERS, + ), + ) + ) + await lk.ingress.update_ingress( + api.UpdateIngressRequest( + ingress_id="IN_abc123", + name="stream-input-v2", + room_name="test-room", + participant_identity="ingress-bot", + participant_name="Live Stream", + enable_transcoding=True, + ) + ) + await lk.ingress.list_ingress( + api.ListIngressRequest(room_name="test-room", ingress_id="IN_abc123") + ) + await lk.ingress.delete_ingress(api.DeleteIngressRequest(ingress_id="IN_abc123")) + + +async def _sip_smoke(): + async with _api() as lk: + await lk.sip.create_inbound_trunk( + api.CreateSIPInboundTrunkRequest( + trunk=api.SIPInboundTrunkInfo( + name="inbound", + metadata='{"provider":"telco"}', + numbers=["+15105550100"], + allowed_addresses=["203.0.113.0/24"], + allowed_numbers=["+15105550111"], + auth_username="sip-user", + auth_password="sip-pass", + krisp_enabled=True, + ) + ) + ) + await lk.sip.create_outbound_trunk( + api.CreateSIPOutboundTrunkRequest( + trunk=api.SIPOutboundTrunkInfo( + name="outbound", + address="sip.telco.example.com", + transport=api.SIPTransport.SIP_TRANSPORT_TLS, + destination_country="US", + numbers=["+15105550100"], + auth_username="sip-user", + auth_password="sip-pass", + ) + ) + ) + await lk.sip.update_inbound_trunk( + "ST_abc123", + api.SIPInboundTrunkInfo(name="inbound-v2", numbers=["+15105550100"]), + ) + await lk.sip.update_outbound_trunk( + "ST_abc123", + api.SIPOutboundTrunkInfo( + name="outbound-v2", + address="sip.telco.example.com", + transport=api.SIPTransport.SIP_TRANSPORT_TLS, + numbers=["+15105550100"], + ), + ) + # field-level (kwargs) update helpers + await lk.sip.update_inbound_trunk_fields( + "ST_abc123", name="inbound-v3", metadata="{}", numbers=["+15105550100"] + ) + await lk.sip.update_outbound_trunk_fields( + "ST_abc123", name="outbound-v3", address="sip.telco.example.com" + ) + await lk.sip.list_inbound_trunk( + api.ListSIPInboundTrunkRequest(trunk_ids=["ST_abc123"], numbers=["+15105550100"]) + ) + await lk.sip.list_outbound_trunk(api.ListSIPOutboundTrunkRequest(trunk_ids=["ST_abc123"])) + await lk.sip.delete_trunk(api.DeleteSIPTrunkRequest(sip_trunk_id="ST_abc123")) + await lk.sip.create_dispatch_rule( + api.CreateSIPDispatchRuleRequest( + dispatch_rule=api.SIPDispatchRuleInfo( + name="direct-to-support", + metadata='{"team":"support"}', + trunk_ids=["ST_abc123"], + rule=api.SIPDispatchRule( + dispatch_rule_direct=api.SIPDispatchRuleDirect( + room_name="support", pin="1234" + ) + ), + ) + ) + ) + await lk.sip.update_dispatch_rule( + "SDR_abc123", + api.SIPDispatchRuleInfo( + name="individual-v2", + rule=api.SIPDispatchRule( + dispatch_rule_individual=api.SIPDispatchRuleIndividual(room_prefix="call-") + ), + ), + ) + await lk.sip.update_dispatch_rule_fields("SDR_abc123", name="rule-v3", metadata="{}") + await lk.sip.list_dispatch_rule( + api.ListSIPDispatchRuleRequest( + dispatch_rule_ids=["SDR_abc123"], trunk_ids=["ST_abc123"] + ) + ) + await lk.sip.delete_dispatch_rule( + api.DeleteSIPDispatchRuleRequest(sip_dispatch_rule_id="SDR_abc123") + ) + + +async def _connector_smoke(): + offer = SessionDescription(type="offer", sdp="v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\n") + answer = SessionDescription(type="answer", sdp="v=0\r\no=- 0 0 IN IP4 127.0.0.1\r\n") + async with _api() as lk: + await lk.connector.dial_whatsapp_call( + api.DialWhatsAppCallRequest( + whatsapp_phone_number_id="123456789012345", + whatsapp_to_phone_number="+15105550100", + whatsapp_api_key="wa-secret-key", + whatsapp_cloud_api_version="23.0", + room_name="test-room", + participant_identity="whatsapp-caller", + participant_name="WhatsApp Caller", + destination_country="US", + ringing_timeout=Duration(seconds=30), + ) + ) + await lk.connector.accept_whatsapp_call( + api.AcceptWhatsAppCallRequest( + whatsapp_phone_number_id="123456789012345", + whatsapp_api_key="wa-secret-key", + whatsapp_cloud_api_version="23.0", + whatsapp_call_id="wacid.HBgLABC", + sdp=answer, + room_name="test-room", + participant_identity="whatsapp-callee", + ) + ) + await lk.connector.connect_whatsapp_call( + api.ConnectWhatsAppCallRequest(whatsapp_call_id="wacid.HBgLABC", sdp=offer) + ) + await lk.connector.disconnect_whatsapp_call( + api.DisconnectWhatsAppCallRequest( + whatsapp_call_id="wacid.HBgLABC", + whatsapp_api_key="wa-secret-key", + disconnect_reason=api.DisconnectWhatsAppCallRequest.BUSINESS_INITIATED, + ) + ) + await lk.connector.connect_twilio_call( + api.ConnectTwilioCallRequest( + twilio_call_direction=api.ConnectTwilioCallRequest.TWILIO_CALL_DIRECTION_INBOUND, + room_name="test-room", + participant_identity="twilio-caller", + destination_country="US", + ) + ) + + +async def _agent_dispatch_smoke(): + async with _api() as lk: + await lk.agent_dispatch.create_dispatch( + api.CreateAgentDispatchRequest( + room="test-room", agent_name="inbound-agent", metadata='{"lang":"en"}' + ) + ) + await lk.agent_dispatch.get_dispatch("AD_abc123", "test-room") + await lk.agent_dispatch.list_dispatch("test-room") + await lk.agent_dispatch.delete_dispatch("AD_abc123", "test-room") + + +def test_room_smoke(): + asyncio.run(_room_smoke()) + + +def test_egress_smoke(): + asyncio.run(_egress_smoke()) + + +def test_ingress_smoke(): + asyncio.run(_ingress_smoke()) + + +def test_sip_smoke(): + asyncio.run(_sip_smoke()) + + +def test_connector_smoke(): + asyncio.run(_connector_smoke()) + + +def test_agent_dispatch_smoke(): + asyncio.run(_agent_dispatch_smoke()) + + +# -- deep: create_room round-trip + error propagation ------------------------- + + +async def _create_room_echo(): + async with _api() as lk: + req = api.CreateRoomRequest( + name="echo-room", metadata='{"scene":"lobby"}', empty_timeout=300, max_participants=50 + ) + room = await lk.room.create_room(req) + assert room.name == "echo-room" + assert room.metadata == '{"scene":"lobby"}' + assert room.empty_timeout == 300 + assert room.max_participants == 50 + assert room.sid != "" # placeholder assigned by the mock + + +def test_create_room_echoes_fields(): + asyncio.run(_create_room_echo()) + + +async def _create_room_error(): + mock = {"failRegions": [0], "failStatus": 400, "failTwirpCode": "invalid_argument"} + async with _api(mock) as lk: + with pytest.raises(ServerError) as exc: + await lk.room.create_room(api.CreateRoomRequest(name="test-room")) + assert exc.value.code == "invalid_argument" + + +def test_create_room_propagates_twirp_error(): + asyncio.run(_create_room_error()) + + +# -- deep: SIP participant (delayMs:0 skips the mock's answer wait) ------------ + + +async def _sip_participant(): + async with _api() as lk: + p = await lk.sip.create_sip_participant( + api.CreateSIPParticipantRequest( + sip_trunk_id="ST_abc123", + sip_call_to="+15105550100", + room_name="test-room", + participant_identity="sip-caller", + participant_name="SIP Caller", + participant_metadata='{"source":"pstn"}', + dtmf="1234#", + play_dialtone=True, + max_call_duration=Duration(seconds=3600), + ) + ) + assert p.room_name == "test-room" + assert p.participant_identity == "sip-caller" + + async with _api({"delayMs": 0}) as lk: + await lk.sip.create_sip_participant( + api.CreateSIPParticipantRequest( + sip_trunk_id="ST_abc123", + sip_call_to="+15105550100", + room_name="test-room", + wait_until_answered=True, + ringing_timeout=Duration(seconds=2), + ) + ) + await lk.sip.transfer_sip_participant( + api.TransferSIPParticipantRequest( + room_name="test-room", + participant_identity="sip-caller", + transfer_to="tel:+15105550122", + ringing_timeout=Duration(seconds=2), + ) + ) + + +def test_sip_participant(): + asyncio.run(_sip_participant()) + + +# -- cross-cutting: token auth ------------------------------------------------ + + +async def _token_auth(): + token = api.AccessToken(KEY, SECRET).with_grants(api.VideoGrants(room_create=True)).to_jwt() + async with api.LiveKitAPI.with_token(token, BASE) as lk: + room = await lk.room.create_room(api.CreateRoomRequest(name="token-room")) + assert room.name == "token-room" + + +def test_token_auth(): + asyncio.run(_token_auth()) + + +# -- cross-cutting: SIP call errors surface as SipCallError ------------------- + + +async def _sip_call_error(sip: dict, *, wait: bool = False): + create = api.CreateSIPParticipantRequest( + sip_trunk_id="ST_abc123", sip_call_to="+15105550100", room_name="test-room" + ) + if wait: + create.wait_until_answered = True + create.ringing_timeout.CopyFrom(Duration(seconds=2)) + async with _api({"delayMs": 0, "sipStatus": sip}) as lk: + with pytest.raises(SipCallError) as exc: + await lk.sip.create_sip_participant(create) + return exc.value + + +def test_sip_busy(): + err = asyncio.run(_sip_call_error({"code": 486, "status": "Busy Here"})) + assert isinstance(err, ServerError) + assert err.code == "resource_exhausted" + assert err.sip_status_code == 486 + assert err.sip_status == "Busy Here" + # printable representation makes the failure clear + assert "486" in str(err) and "Busy Here" in str(err) + + +def test_sip_declined(): + err = asyncio.run(_sip_call_error({"code": 603, "status": "Decline"})) + assert err.code == "permission_denied" + assert err.sip_status_code == 603 + + +def test_sip_no_answer(): + # Callee doesn't pick up within ringing_timeout -> SIP 408 Request Timeout. + err = asyncio.run(_sip_call_error({"code": 408, "status": "Request Timeout"}, wait=True)) + assert err.code == "deadline_exceeded" + assert err.sip_status_code == 408 + + +# -- cross-cutting: client-side dial timeout ---------------------------------- + + +async def _sip_dial_timeout(): + # ringing 1s -> ~3s dial budget; the mock delays the answer past it. + async with _api({"delayMs": 4000}) as lk: + await lk.sip.create_sip_participant( + api.CreateSIPParticipantRequest( + sip_trunk_id="ST_abc123", + sip_call_to="+15105550100", + room_name="test-room", + wait_until_answered=True, + ringing_timeout=Duration(seconds=1), + ) + ) + + +def test_sip_dial_timeout(): + with pytest.raises((asyncio.TimeoutError, TimeoutError)): + asyncio.run(_sip_dial_timeout()) diff --git a/livekit-api/tests/test_webhook.py b/tests/api/test_webhook.py similarity index 94% rename from livekit-api/tests/test_webhook.py rename to tests/api/test_webhook.py index fc8828e5..3937cdba 100644 --- a/livekit-api/tests/test_webhook.py +++ b/tests/api/test_webhook.py @@ -1,7 +1,7 @@ import base64 import hashlib -import pytest # type: ignore +import pytest from livekit.api import AccessToken, TokenVerifier, WebhookReceiver TEST_API_KEY = "myapikey" @@ -45,7 +45,7 @@ """ -def test_webhook_receiver(): +def test_webhook_receiver() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) @@ -56,7 +56,7 @@ def test_webhook_receiver(): receiver.receive(TEST_EVENT, jwt) -def test_bad_hash(): +def test_bad_hash() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) @@ -68,7 +68,7 @@ def test_bad_hash(): receiver.receive(TEST_EVENT, jwt) -def test_invalid_body(): +def test_invalid_body() -> None: token_verifier = TokenVerifier(TEST_API_KEY, TEST_API_SECRET) receiver = WebhookReceiver(token_verifier) diff --git a/tests/rtc/fixtures/.gitattributes b/tests/rtc/fixtures/.gitattributes new file mode 100644 index 00000000..d899f655 --- /dev/null +++ b/tests/rtc/fixtures/.gitattributes @@ -0,0 +1 @@ +*.wav filter=lfs diff=lfs merge=lfs -text diff --git a/tests/rtc/fixtures/test_audio.wav b/tests/rtc/fixtures/test_audio.wav new file mode 100644 index 00000000..70e28398 --- /dev/null +++ b/tests/rtc/fixtures/test_audio.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ac5cba8b2477ab55c9bc5f95faffcd3d9b9ad4e6a01d79308fbe6eeef733ce80 +size 534606 diff --git a/tests/rtc/fixtures/test_echo_capture.wav b/tests/rtc/fixtures/test_echo_capture.wav new file mode 100644 index 00000000..14035d71 --- /dev/null +++ b/tests/rtc/fixtures/test_echo_capture.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:90626c2c532dfb8313ae52501a9500f1c90235570aab8ff1367e7f91fb697a7e +size 970796 diff --git a/tests/rtc/fixtures/test_echo_render.wav b/tests/rtc/fixtures/test_echo_render.wav new file mode 100644 index 00000000..3d061ce6 --- /dev/null +++ b/tests/rtc/fixtures/test_echo_render.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf50f57f00fa941ab612c57c24a28811b93c878d3c98edcb4a8f21508aa8e566 +size 970796 diff --git a/tests/rtc/fixtures/test_processed.wav b/tests/rtc/fixtures/test_processed.wav new file mode 100644 index 00000000..c8520e86 --- /dev/null +++ b/tests/rtc/fixtures/test_processed.wav @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8a48eb2f6a2143b4bc6adfe306983637f628fffc9d550c60b4ebcecd506d6245 +size 971564 diff --git a/tests/rtc/test_agc.py b/tests/rtc/test_agc.py new file mode 100644 index 00000000..15a57f58 --- /dev/null +++ b/tests/rtc/test_agc.py @@ -0,0 +1,60 @@ +import os +import wave +import numpy as np + +from livekit.rtc import AudioProcessingModule, AudioFrame + +FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures") + + +def test_agc_modifies_audio() -> None: + num_channels = 1 + + input_wav = os.path.join(FIXTURES_DIR, "test_audio.wav") + output_wav = os.path.join(FIXTURES_DIR, "test_processed.wav") + + apm = AudioProcessingModule(auto_gain_control=True) + + any_frame_modified = False + + with wave.open(input_wav, "rb") as wf_in: + assert wf_in.getnchannels() == num_channels, "Input file must be mono." + sample_rate = wf_in.getframerate() + sampwidth = wf_in.getsampwidth() + frames_per_chunk = sample_rate // 100 + + with wave.open(output_wav, "wb") as wf_out: + wf_out.setnchannels(num_channels) + wf_out.setsampwidth(sampwidth) + wf_out.setframerate(sample_rate) + + while True: + raw_bytes = wf_in.readframes(frames_per_chunk) + if not raw_bytes: + break + + data = np.frombuffer(raw_bytes, dtype=np.int16) + if len(data) < frames_per_chunk: + data = np.pad(data, (0, frames_per_chunk - len(data))) + + original = data.copy() + + frame = AudioFrame( + data=data.tobytes(), + sample_rate=sample_rate, + num_channels=num_channels, + samples_per_channel=frames_per_chunk, + ) + + apm.process_stream(frame) + + processed = np.frombuffer(frame.data, dtype=np.int16) + if not np.array_equal(original, processed): + any_frame_modified = True + + wf_out.writeframes(frame.data.tobytes()) + + assert any_frame_modified, ( + "APM did not modify any audio frames — processing may be a no-op. " + "With AGC enabled, output should differ from input." + ) diff --git a/tests/rtc/test_apm.py b/tests/rtc/test_apm.py new file mode 100644 index 00000000..df5cd41d --- /dev/null +++ b/tests/rtc/test_apm.py @@ -0,0 +1,80 @@ +import os +import wave +import numpy as np + +from livekit.rtc import AudioProcessingModule, AudioFrame + +# Test fixture directory +FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures") + + +def test_audio_processing() -> None: + sample_rate = 48000 + num_channels = 1 + frames_per_chunk = sample_rate // 100 + + capture_wav = os.path.join(FIXTURES_DIR, "test_echo_capture.wav") + render_wav = os.path.join(FIXTURES_DIR, "test_echo_render.wav") + output_wav = os.path.join(FIXTURES_DIR, "test_processed.wav") + + # Initialize APM with echo cancellation enabled + apm = AudioProcessingModule( + echo_cancellation=True, + noise_suppression=True, + high_pass_filter=True, + auto_gain_control=True, + ) + print("APM Internal Handle:", apm._ffi_handle) + + with ( + wave.open(capture_wav, "rb") as wf_in_cap, + wave.open(render_wav, "rb") as wf_in_rend, + wave.open(output_wav, "wb") as wf_out, + ): + assert wf_in_cap.getnchannels() == num_channels, "Capture file must be mono." + assert wf_in_rend.getnchannels() == num_channels, "Render file must be mono." + assert wf_in_cap.getframerate() == sample_rate, "Capture file must be 48 kHz." + assert wf_in_rend.getframerate() == sample_rate, "Render file must be 48 kHz." + + sampwidth = wf_in_cap.getsampwidth() + wf_out.setnchannels(num_channels) + wf_out.setsampwidth(sampwidth) + wf_out.setframerate(sample_rate) + + while True: + capture_bytes = wf_in_cap.readframes(frames_per_chunk) + render_bytes = wf_in_rend.readframes(frames_per_chunk) + + if not capture_bytes and not render_bytes: + break + + # Convert bytes to numpy arrays + capture_data = np.frombuffer(capture_bytes, dtype=np.int16) + render_data = np.frombuffer(render_bytes, dtype=np.int16) + + # Pad if necessary + if len(capture_data) < frames_per_chunk: + capture_data = np.pad(capture_data, (0, frames_per_chunk - len(capture_data))) + if len(render_data) < frames_per_chunk: + render_data = np.pad(render_data, (0, frames_per_chunk - len(render_data))) + + capture_frame = AudioFrame( + data=capture_data.tobytes(), + sample_rate=sample_rate, + num_channels=num_channels, + samples_per_channel=frames_per_chunk, + ) + render_frame = AudioFrame( + data=render_data.tobytes(), + sample_rate=sample_rate, + num_channels=num_channels, + samples_per_channel=frames_per_chunk, + ) + + # Process both streams + apm.process_reverse_stream(render_frame) + apm.process_stream(capture_frame) + + wf_out.writeframes(capture_frame.data.tobytes()) + + print("Done! Processed audio saved to:", output_wav) diff --git a/tests/rtc/test_connection_quality.py b/tests/rtc/test_connection_quality.py new file mode 100644 index 00000000..14553961 --- /dev/null +++ b/tests/rtc/test_connection_quality.py @@ -0,0 +1,29 @@ +"""Unit tests for the local ConnectionQuality enum and the participant property.""" + +from livekit import rtc +from livekit.rtc._proto import participant_pb2 as proto_participant +from livekit.rtc._proto import room_pb2 as proto_room +from livekit.rtc.participant import _connection_quality_from_proto + + +def test_enum_values_align_with_proto() -> None: + CQ = rtc.ConnectionQuality + assert CQ.QUALITY_UNKNOWN == -1 + assert CQ.QUALITY_POOR == proto_room.ConnectionQuality.QUALITY_POOR + assert CQ.QUALITY_GOOD == proto_room.ConnectionQuality.QUALITY_GOOD + assert CQ.QUALITY_EXCELLENT == proto_room.ConnectionQuality.QUALITY_EXCELLENT + assert CQ.QUALITY_LOST == proto_room.ConnectionQuality.QUALITY_LOST + # backward compat: still int-comparable like the old proto enum export + assert CQ.QUALITY_GOOD == 1 + + +def test_from_proto_mapping() -> None: + assert _connection_quality_from_proto(1) is rtc.ConnectionQuality.QUALITY_GOOD + assert _connection_quality_from_proto(99) is rtc.ConnectionQuality.QUALITY_UNKNOWN + + +def test_participant_default_quality_is_unknown() -> None: + owned = proto_participant.OwnedParticipant() + owned.info.identity = "test" + participant = rtc.RemoteParticipant(owned) + assert participant.connection_quality is rtc.ConnectionQuality.QUALITY_UNKNOWN diff --git a/tests/rtc/test_e2e.py b/tests/rtc/test_e2e.py new file mode 100644 index 00000000..3fff81f4 --- /dev/null +++ b/tests/rtc/test_e2e.py @@ -0,0 +1,684 @@ +""" +End-to-end tests for LiveKit RTC library. + +These tests verify core functionality of the LiveKit RTC library including: +- Publishing and subscribing to audio & data tracks +- Audio stream consumption and energy verification +- Room lifecycle events (connect, disconnect, track publish/unpublish) +- Connection state transitions + +Requirements: +- LIVEKIT_URL: LiveKit server URL +- LIVEKIT_API_KEY: API key for authentication +- LIVEKIT_API_SECRET: API secret for authentication + +Tests will be skipped if these environment variables are not set. + +Usage: + pytest test_e2e.py -v +""" + +import asyncio +import os +import time +import uuid +from typing import Any, Callable, Dict, List, TypeVar +import numpy as np +import pytest + +from livekit import rtc, api +from livekit.rtc.utils import sine_wave_generator + + +SAMPLE_RATE = 48000 +T = TypeVar("T") + + +async def assert_eventually( + condition: Callable[[], T], + timeout: float = 5.0, + interval: float = 0.1, + message: str = "Condition not met within timeout", +) -> T: + """ + Poll a condition until it becomes truthy or timeout is reached. + Returns immediately once condition is satisfied. + """ + deadline = asyncio.get_event_loop().time() + timeout + last_result = None + + while asyncio.get_event_loop().time() < deadline: + last_result = condition() + if last_result: + return last_result + await asyncio.sleep(interval) + + raise AssertionError(f"{message} (last result: {last_result})") + + +def skip_if_no_credentials() -> Any: + required_vars = ["LIVEKIT_URL", "LIVEKIT_API_KEY", "LIVEKIT_API_SECRET"] + missing = [var for var in required_vars if not os.getenv(var)] + return pytest.mark.skipif( + bool(missing), reason=f"Missing environment variables: {', '.join(missing)}" + ) + + +def create_token(identity: str, room_name: str) -> str: + return ( + api.AccessToken() + .with_identity(identity) + .with_name(identity) + .with_grants( + api.VideoGrants( + room_join=True, + room=room_name, + ) + ) + .to_jwt() + ) + + +def unique_room_name(base: str) -> str: + return f"{base}-{uuid.uuid4().hex[:8]}" + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_publish_track() -> None: + """Test that a published track can be subscribed by another participant""" + room_name = unique_room_name("test-publish-track") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token("publisher", room_name) + subscriber_token = create_token("subscriber", room_name) + + track_published_event = asyncio.Event() + track_subscribed_event = asyncio.Event() + subscribed_track = None + + @subscriber_room.on("track_published") + def on_track_published( + publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant + ) -> None: + track_published_event.set() + + @subscriber_room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal subscribed_track + if track.kind == rtc.TrackKind.KIND_AUDIO: + subscribed_track = track + track_subscribed_event.set() + + try: + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + source = rtc.AudioSource(SAMPLE_RATE, 1) + track = rtc.LocalAudioTrack.create_audio_track("test-audio", source) + options = rtc.TrackPublishOptions() + options.source = rtc.TrackSource.SOURCE_MICROPHONE + publication = await publisher_room.local_participant.publish_track(track, options) + + assert publication is not None + assert publication.sid is not None + + await asyncio.wait_for(track_published_event.wait(), timeout=5.0) + await asyncio.wait_for(track_subscribed_event.wait(), timeout=5.0) + + assert subscribed_track is not None + assert isinstance(subscribed_track, rtc.RemoteAudioTrack) + + finally: + await publisher_room.disconnect() + await subscriber_room.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_video_frame_metadata() -> None: + """Test that frame metadata can be sent and received on video frames.""" + room_name = unique_room_name("test-video-frame-metadata") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token("video-publisher", room_name) + subscriber_token = create_token("video-subscriber", room_name) + + track_subscribed_event = asyncio.Event() + subscribed_track = None + subscribed_publication = None + video_stream = None + source = None + + @subscriber_room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal subscribed_track, subscribed_publication + if track.kind == rtc.TrackKind.KIND_VIDEO: + subscribed_track = track + subscribed_publication = publication + track_subscribed_event.set() + + try: + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + # Use a real video resolution: WebRTC encoders (VP8/H.264) enforce a + # 16x16 minimum and assert internally on smaller inputs, which would + # crash the FFI process via panic=abort. + frame_width, frame_height = 320, 240 + source = rtc.VideoSource(frame_width, frame_height) + track = rtc.LocalVideoTrack.create_video_track("metadata-video", source) + frame_metadata_features = [ + rtc.FrameMetadataFeature.FMF_USER_TIMESTAMP, + rtc.FrameMetadataFeature.FMF_FRAME_ID, + ] + options = rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + frame_metadata_features=frame_metadata_features, + ) + publication = await publisher_room.local_participant.publish_track(track, options) + + assert publication.frame_metadata_features == frame_metadata_features + await asyncio.wait_for(track_subscribed_event.wait(), timeout=5.0) + assert subscribed_track is not None + assert subscribed_publication is not None + assert subscribed_publication.frame_metadata_features == frame_metadata_features + + video_stream = rtc.VideoStream.from_track(track=subscribed_track, capacity=1) + frame = rtc.VideoFrame( + width=frame_width, + height=frame_height, + type=rtc.VideoBufferType.RGBA, + data=bytes([255, 0, 0, 255] * (frame_width * frame_height)), + ) + metadata = rtc.FrameMetadata(user_timestamp=123456789, frame_id=77) + + async def publish_frames() -> None: + for _ in range(10): + source.capture_frame(frame, metadata=metadata) + await asyncio.sleep(0.2) + + publish_task = asyncio.create_task(publish_frames()) + event = await asyncio.wait_for(video_stream.__anext__(), timeout=10.0) + await publish_task + + assert event.metadata is not None + assert event.metadata.HasField("user_timestamp") + assert event.metadata.HasField("frame_id") + assert event.metadata.user_timestamp == 123456789 + assert event.metadata.frame_id == 77 + + finally: + if video_stream is not None: + await video_stream.aclose() + if source is not None: + await source.aclose() + await publisher_room.disconnect() + await subscriber_room.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_full_reconnect_preserves_local_publication_object() -> None: + """Test that FFI local_track_republished updates the existing publication object.""" + room_name = unique_room_name("test-local-republish") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + room = rtc.Room() + token = create_token("publisher", room_name) + reconnected_event = asyncio.Event() + source = None + + @room.on("reconnected") + def on_reconnected() -> None: + reconnected_event.set() + + try: + await room.connect(url, token) + + source = rtc.VideoSource(2, 2) + track = rtc.LocalVideoTrack.create_video_track("republish-video", source) + frame_metadata_features = [ + rtc.FrameMetadataFeature.FMF_USER_TIMESTAMP, + rtc.FrameMetadataFeature.FMF_FRAME_ID, + ] + publication = await room.local_participant.publish_track( + track, + rtc.TrackPublishOptions( + source=rtc.TrackSource.SOURCE_CAMERA, + frame_metadata_features=frame_metadata_features, + ), + ) + previous_sid = publication.sid + + await room.simulate_scenario(rtc.SimulateScenarioKind.SIMULATE_FULL_RECONNECT) + await asyncio.wait_for(reconnected_event.wait(), timeout=10.0) + + await assert_eventually( + lambda: ( + publication.sid in room.local_participant.track_publications + and room.local_participant.track_publications[publication.sid] is publication + ), + timeout=10.0, + message="local publication was not rekeyed after full reconnect", + ) + + assert publication.sid != previous_sid + assert previous_sid not in room.local_participant.track_publications + assert publication.frame_metadata_features == frame_metadata_features + + finally: + if source is not None: + await source.aclose() + await room.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_audio_stream_subscribe() -> None: + """Test that published audio can be consumed and has similar energy levels""" + room_name = unique_room_name("test-audio-stream") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token("audio-publisher", room_name) + subscriber_token = create_token("audio-subscriber", room_name) + + track_subscribed_event = asyncio.Event() + subscribed_track = None + + @subscriber_room.on("track_subscribed") + def on_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + nonlocal subscribed_track + if track.kind == rtc.TrackKind.KIND_AUDIO: + subscribed_track = track + track_subscribed_event.set() + + try: + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + source = rtc.AudioSource(SAMPLE_RATE, 1) + track = rtc.LocalAudioTrack.create_audio_track("sine-wave", source) + options = rtc.TrackPublishOptions() + options.source = rtc.TrackSource.SOURCE_MICROPHONE + await publisher_room.local_participant.publish_track(track, options) + target_duration = 5.0 + + published_energy: List[Any] = [] + + async def publish_audio() -> None: + async for frame in sine_wave_generator(440, target_duration, SAMPLE_RATE): + data = np.frombuffer(frame.data.tobytes(), dtype=np.int16) + energy = np.mean(np.abs(data.astype(np.float32))) + published_energy.append(energy) + await source.capture_frame(frame) + + publish_task = asyncio.create_task(publish_audio()) + + await asyncio.wait_for(track_subscribed_event.wait(), timeout=5.0) + assert subscribed_track is not None + + audio_stream = rtc.AudioStream( + subscribed_track, + sample_rate=SAMPLE_RATE, + num_channels=1, + ) + + received_frames = [] + target_frames = int(target_duration * SAMPLE_RATE / 480) + + frame_count = 0 + async for event in audio_stream: + frame = event.frame + data = np.frombuffer(frame.data, dtype=np.int16) + received_frames.append(data) + frame_count += 1 + if frame_count >= target_frames: + break + + await audio_stream.aclose() + await publish_task + + assert len(received_frames) > 0, "No audio frames were received" + + received_energy = [] + for data in received_frames: + energy = np.mean(np.abs(data.astype(np.float32))) + received_energy.append(energy) + + avg_received_energy = np.mean(received_energy) + avg_published_energy = np.mean(published_energy) + + assert avg_received_energy > 0, "Received audio has no energy" + assert avg_published_energy > 0, "Published audio has no energy" + assert ( + avg_received_energy > avg_published_energy * 0.9 + and avg_received_energy < avg_published_energy * 1.1 + ), "Received audio energy is not within range" + + finally: + await publisher_room.disconnect() + await subscriber_room.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_room_lifecycle_events() -> None: + """Test that room lifecycle and track events are fired properly""" + room_name = unique_room_name("test-lifecycle-events") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + room1 = rtc.Room() + room2 = rtc.Room() + + token1 = create_token("participant-1", room_name) + token2 = create_token("participant-2", room_name) + + events: Dict[str, List[str]] = { + "disconnected": [], + "participant_connected": [], + "participant_disconnected": [], + "local_track_published": [], + "local_track_unpublished": [], + "track_published": [], + "track_unpublished": [], + "track_subscribed": [], + "track_unsubscribed": [], + "room_updated": [], + "connection_state_changed": [], + } + + @room1.on("disconnected") + def on_room1_disconnected(reason: Any) -> None: + events["disconnected"].append("room1") + + @room1.on("participant_connected") + def on_room1_participant_connected(participant: rtc.RemoteParticipant) -> None: + events["participant_connected"].append(f"room1-{participant.identity}") + + @room1.on("participant_disconnected") + def on_room1_participant_disconnected(participant: rtc.RemoteParticipant) -> None: + events["participant_disconnected"].append(f"room1-{participant.identity}") + + @room1.on("local_track_published") + def on_room1_local_track_published(publication: rtc.LocalTrackPublication, track: Any) -> None: + events["local_track_published"].append(f"room1-{publication.sid}") + + @room1.on("local_track_unpublished") + def on_room1_local_track_unpublished(publication: rtc.LocalTrackPublication) -> None: + events["local_track_unpublished"].append(f"room1-{publication.sid}") + + @room1.on("room_updated") + def on_room1_room_updated() -> None: + events["room_updated"].append("room1") + + @room1.on("connection_state_changed") + def on_room1_connection_state_changed(state: rtc.ConnectionState) -> None: + events["connection_state_changed"].append(f"room1-{state}") + + @room2.on("track_published") + def on_room2_track_published( + publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant + ) -> None: + events["track_published"].append(f"room2-{publication.sid}") + + @room2.on("track_subscribed") + def on_room2_track_subscribed( + track: rtc.Track, + publication: rtc.RemoteTrackPublication, + participant: rtc.RemoteParticipant, + ) -> None: + events["track_subscribed"].append(f"room2-{publication.sid}") + + @room2.on("track_unpublished") + def on_room2_track_unpublished( + publication: rtc.RemoteTrackPublication, participant: rtc.RemoteParticipant + ) -> None: + events["track_unpublished"].append(f"room2-{publication.sid}") + + try: + await room1.connect(url, token1) + + await assert_eventually( + lambda: ( + len(events["connection_state_changed"]) > 0 + and events["connection_state_changed"][-1] + == f"room1-{rtc.ConnectionState.CONN_CONNECTED}" + ), + message="room1 connection_state_changed event not fired or did not reach CONN_CONNECTED state", + ) + + await room2.connect(url, token2) + + await assert_eventually( + lambda: "room1-participant-2" in events["participant_connected"], + message="room1 did not receive participant_connected for participant-2", + ) + await assert_eventually( + lambda: room2.remote_participants.get("participant-1") is not None, + message="room2 did not see participant-1", + ) + + source = rtc.AudioSource(SAMPLE_RATE, 1) + track = rtc.LocalAudioTrack.create_audio_track("test-track", source) + options = rtc.TrackPublishOptions() + options.source = rtc.TrackSource.SOURCE_MICROPHONE + publication = await room1.local_participant.publish_track(track, options) + + await assert_eventually( + lambda: len(events["local_track_published"]) > 0, + message="local_track_published event not fired", + ) + await assert_eventually( + lambda: any("room2" in e for e in events["track_published"]), + message="room2 did not receive track_published", + ) + await assert_eventually( + lambda: len(events["track_subscribed"]) > 0, message="track_subscribed event not fired" + ) + + await room1.local_participant.unpublish_track(publication.sid) + + await assert_eventually( + lambda: len(events["local_track_unpublished"]) > 0, + message="local_track_unpublished event not fired", + ) + await assert_eventually( + lambda: len(events["track_unpublished"]) > 0, + message="track_unpublished event not fired", + ) + + await room2.disconnect() + + await assert_eventually( + lambda: "room1-participant-2" in events["participant_disconnected"], + message="participant_disconnected not fired for participant-2", + ) + + await room1.disconnect() + + await assert_eventually( + lambda: ( + lambda: ( + len(events["connection_state_changed"]) > 0 + and events["connection_state_changed"][-1] + == f"room1-{rtc.ConnectionState.CONN_DISCONNECTED}" + ) + ), + message="room1 disconnected event not fired", + ) + + print("\nEvent Summary:") + for event_type, event_list in events.items(): + if event_list: + print(f" {event_type}: {len(event_list)} events") + + finally: + if room1.isconnected(): + await room1.disconnect() + if room2.isconnected(): + await room2.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +async def test_connection_state_transitions() -> None: + """Test that connection state transitions work correctly""" + room_name = unique_room_name("test-connection-state") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + room = rtc.Room() + token = create_token("state-test", room_name) + + states: List[rtc.ConnectionState] = [] + + @room.on("connection_state_changed") + def on_state_changed(state: rtc.ConnectionState) -> None: + states.append(state) + + try: + assert room.connection_state == rtc.ConnectionState.CONN_DISCONNECTED + + await room.connect(url, token) + + await assert_eventually( + lambda: room.connection_state == rtc.ConnectionState.CONN_CONNECTED, + message="Room did not reach CONN_CONNECTED state", + ) + await assert_eventually( + lambda: rtc.ConnectionState.CONN_CONNECTED in states, # type: ignore[comparison-overlap] + message="CONN_CONNECTED state not in state change events", + ) + + await room.disconnect() + + await assert_eventually( + lambda: room.connection_state == rtc.ConnectionState.CONN_DISCONNECTED, + message="Room did not reach CONN_DISCONNECTED state after disconnect", + ) + + finally: + if room.isconnected(): + await room.disconnect() + + +@pytest.mark.asyncio +@skip_if_no_credentials() # type: ignore[untyped-decorator] +@pytest.mark.skipif( + os.getenv("RUN_DATA_TRACK_TESTS") != "1", + reason="SFU support requires data tracks support to be enabled via config; remove once this is no longer the case.", +) +async def test_data_track() -> None: + """Test that a published data track delivers frames with correct payloads and timestamps.""" + FRAME_COUNT = 5 + PAYLOAD_SIZE = 64 + + TRACK_NAME = "test-track" + PUBLISHER_IDENTITY = "dt-publisher" + SUBSCRIBER_IDENTITY = "dt-subscriber" + + room_name = unique_room_name("test-data-track") + url = os.getenv("LIVEKIT_URL") + assert url is not None + + publisher_room = rtc.Room() + subscriber_room = rtc.Room() + + publisher_token = create_token(PUBLISHER_IDENTITY, room_name) + subscriber_token = create_token(SUBSCRIBER_IDENTITY, room_name) + + remote_track_event = asyncio.Event() + remote_track: None | rtc.RemoteDataTrack = None + unpublished_event = asyncio.Event() + unpublished_sid = None + + @subscriber_room.on("data_track_published") + def on_data_track_published(track: rtc.RemoteDataTrack) -> None: + nonlocal remote_track + remote_track = track + remote_track_event.set() + + @subscriber_room.on("data_track_unpublished") + def on_data_track_unpublished(sid: str) -> None: + nonlocal unpublished_sid + unpublished_sid = sid + unpublished_event.set() + + try: + assert url is not None + await subscriber_room.connect(url, subscriber_token) + await publisher_room.connect(url, publisher_token) + + local_track = await publisher_room.local_participant.publish_data_track(name=TRACK_NAME) + assert local_track.info.sid is not None + assert local_track.info.name == TRACK_NAME + assert local_track.is_published() + + await asyncio.wait_for(remote_track_event.wait(), timeout=10.0) + assert remote_track is not None + assert remote_track.info.name == TRACK_NAME + assert remote_track.publisher_identity == PUBLISHER_IDENTITY + assert remote_track.is_published() + + stream = remote_track.subscribe() + + async def push_frames() -> None: + for i in range(FRAME_COUNT): + frame = rtc.DataTrackFrame( + payload=bytes([i] * PAYLOAD_SIZE), + user_timestamp=int(time.time() * 1000), + ) + local_track.try_push(frame) + await asyncio.sleep(0.1) + await local_track.unpublish() + + async def publish_and_receive(stream: rtc.DataTrackStream) -> int: + push_task = asyncio.create_task(push_frames()) + recv_count = 0 + async for frame in stream: + first_byte = frame.payload[0] + assert all(b == first_byte for b in frame.payload), "Payload bytes are not uniform" + assert len(frame.payload) == PAYLOAD_SIZE + assert frame.user_timestamp is not None + latency = (int(time.time() * 1000) - frame.user_timestamp) / 1000.0 + assert latency < 5.0, f"Timestamp latency too high: {latency}" + recv_count += 1 + await push_task + return recv_count + + recv_count = await asyncio.wait_for(publish_and_receive(stream), timeout=10.0) + assert recv_count > 0, "No frames were received" + + await asyncio.wait_for(unpublished_event.wait(), timeout=5.0) + assert unpublished_sid == local_track.info.sid + + finally: + await publisher_room.disconnect() + await subscriber_room.disconnect() diff --git a/tests/rtc/test_emitter.py b/tests/rtc/test_emitter.py new file mode 100644 index 00000000..b5a1bf59 --- /dev/null +++ b/tests/rtc/test_emitter.py @@ -0,0 +1,104 @@ +from livekit.rtc import EventEmitter +from typing import Any, Literal +import pytest + + +def test_events() -> None: + EventTypes = Literal["connected", "reconnected", "disconnected"] + emitter = EventEmitter[EventTypes]() + + connected_calls = [] + + @emitter.once("connected") + def on_connected() -> None: + connected_calls.append(True) + + emitter.emit("connected") + emitter.emit("connected") + assert len(connected_calls) == 1 + + emitter.emit("unknown_event") # type: ignore + + reconnected_calls = [] + + @emitter.on("reconnected") + def on_reconnected() -> None: + reconnected_calls.append(True) + + emitter.emit("reconnected") + emitter.emit("reconnected") + assert len(reconnected_calls) == 2 + + disconnected_calls = [] + + @emitter.on("disconnected") + def on_disconnected() -> None: + disconnected_calls.append(True) + + @emitter.on("disconnected") + def on_disconnected_another() -> None: + disconnected_calls.append(True) + + emitter.emit("disconnected") + emitter.emit("disconnected") + emitter.off("disconnected", on_disconnected) + emitter.emit("disconnected") + assert len(disconnected_calls) == 5 + + +def test_args() -> None: + EventTypes = Literal["whatever"] + + emitter = EventEmitter[EventTypes]() + + calls = [] + + @emitter.on("whatever") + def on_whatever(first: Any, second: Any, third: Any) -> None: + calls.append((first, second, third)) + + emitter.emit("whatever", 1, 2, 3) + emitter.emit("whatever", 1, 2, 3, 4, 5) # only 3 arguments will be passed + + assert calls == [(1, 2, 3), (1, 2, 3)] + + with pytest.raises(TypeError): + emitter.emit("whatever", 1, 2) + + +def test_varargs() -> None: + EventTypes = Literal["whatever"] + + emitter = EventEmitter[EventTypes]() + + calls = [] + + @emitter.on("whatever") + def on_whatever_varargs(*args: Any) -> None: + calls.append(args) + + emitter.emit("whatever", 1, 2, 3, 4, 5) + emitter.emit("whatever", 1, 2) + + assert calls == [(1, 2, 3, 4, 5), (1, 2)] + + +def test_throw() -> None: + EventTypes = Literal["error"] + + emitter = EventEmitter[EventTypes]() + + calls = [] + + @emitter.on("error") + def on_error() -> None: + calls.append(True) + raise ValueError("error") + + @emitter.on("error") + def on_error_another() -> None: + calls.append(True) + + emitter.emit("error") + + assert len(calls) == 2 diff --git a/tests/rtc/test_ffi_queue.py b/tests/rtc/test_ffi_queue.py new file mode 100644 index 00000000..5f76aa6e --- /dev/null +++ b/tests/rtc/test_ffi_queue.py @@ -0,0 +1,268 @@ +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Tests for FfiQueue filter_fn functionality. + +These tests verify the filter_fn feature of FfiQueue. +FfiQueue can be imported without loading the native FFI library. +""" + +import asyncio +from collections.abc import Generator +from dataclasses import dataclass + +import pytest + +from livekit.rtc._ffi_client import FfiQueue + + +@dataclass +class MockFfiEvent: + """Mock FFI event with WhichOneof support.""" + + _message_type: str + + def WhichOneof(self, field: str) -> str: + return self._message_type + + +class TestFfiQueueFilterFn: + """Test suite for FfiQueue filter_fn functionality.""" + + @pytest.fixture + def event_loop(self) -> Generator[asyncio.AbstractEventLoop, None, None]: + """Create event loop for tests.""" + loop = asyncio.new_event_loop() + yield loop + loop.close() + + def test_subscribe_without_filter_receives_all_events( + self, event_loop: asyncio.AbstractEventLoop + ) -> None: + """Subscriber without filter_fn receives all events.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + sub = queue.subscribe(event_loop, filter_fn=None) + + events = [ + MockFfiEvent("room_event"), + MockFfiEvent("audio_stream_event"), + MockFfiEvent("video_stream_event"), + MockFfiEvent("track_event"), + ] + + for event in events: + queue.put(event) + + event_loop.run_until_complete(asyncio.sleep(0.01)) + + received = [] + while not sub.empty(): + received.append(sub.get_nowait()) + + assert len(received) == 4 + + def test_subscribe_with_filter_receives_only_matching_events( + self, event_loop: asyncio.AbstractEventLoop + ) -> None: + """Subscriber with filter_fn only receives matching events.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + sub = queue.subscribe( + event_loop, + filter_fn=lambda e: e.WhichOneof("message") == "audio_stream_event", + ) + + events = [ + MockFfiEvent("room_event"), + MockFfiEvent("audio_stream_event"), + MockFfiEvent("video_stream_event"), + MockFfiEvent("audio_stream_event"), + MockFfiEvent("track_event"), + ] + + for event in events: + queue.put(event) + + event_loop.run_until_complete(asyncio.sleep(0.01)) + + received = [] + while not sub.empty(): + received.append(sub.get_nowait()) + + assert len(received) == 2 + assert all(e._message_type == "audio_stream_event" for e in received) + + def test_multiple_subscribers_different_filters( + self, event_loop: asyncio.AbstractEventLoop + ) -> None: + """Multiple subscribers can have different filters.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + + sub_audio = queue.subscribe( + event_loop, + filter_fn=lambda e: e.WhichOneof("message") == "audio_stream_event", + ) + sub_video = queue.subscribe( + event_loop, + filter_fn=lambda e: e.WhichOneof("message") == "video_stream_event", + ) + sub_all = queue.subscribe(event_loop, filter_fn=None) + + events = [ + MockFfiEvent("room_event"), + MockFfiEvent("audio_stream_event"), + MockFfiEvent("video_stream_event"), + MockFfiEvent("audio_stream_event"), + ] + + for event in events: + queue.put(event) + + event_loop.run_until_complete(asyncio.sleep(0.01)) + + audio_count = 0 + while not sub_audio.empty(): + sub_audio.get_nowait() + audio_count += 1 + + video_count = 0 + while not sub_video.empty(): + sub_video.get_nowait() + video_count += 1 + + all_count = 0 + while not sub_all.empty(): + sub_all.get_nowait() + all_count += 1 + + assert audio_count == 2 + assert video_count == 1 + assert all_count == 4 + + def test_filter_with_multiple_event_types(self, event_loop: asyncio.AbstractEventLoop) -> None: + """Filter can match multiple event types.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + sub = queue.subscribe( + event_loop, + filter_fn=lambda e: ( + e.WhichOneof("message") in {"audio_stream_event", "video_stream_event"} + ), + ) + + events = [ + MockFfiEvent("room_event"), + MockFfiEvent("audio_stream_event"), + MockFfiEvent("video_stream_event"), + MockFfiEvent("track_event"), + ] + + for event in events: + queue.put(event) + + event_loop.run_until_complete(asyncio.sleep(0.01)) + + received = [] + while not sub.empty(): + received.append(sub.get_nowait()) + + assert len(received) == 2 + types = {e._message_type for e in received} + assert types == {"audio_stream_event", "video_stream_event"} + + def test_unsubscribe_works_with_filtered_subscriber( + self, event_loop: asyncio.AbstractEventLoop + ) -> None: + """Unsubscribe correctly removes filtered subscriber.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + sub = queue.subscribe( + event_loop, + filter_fn=lambda e: e.WhichOneof("message") == "audio_stream_event", + ) + + queue.put(MockFfiEvent("audio_stream_event")) + event_loop.run_until_complete(asyncio.sleep(0.01)) + + assert not sub.empty() + + queue.unsubscribe(sub) + + while not sub.empty(): + sub.get_nowait() + + queue.put(MockFfiEvent("audio_stream_event")) + event_loop.run_until_complete(asyncio.sleep(0.01)) + + assert sub.empty() + + def test_filter_error_delivers_item(self, event_loop: asyncio.AbstractEventLoop) -> None: + """If filter_fn raises, item is still delivered.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + + def bad_filter(e: MockFfiEvent) -> bool: + raise ValueError("oops") + + sub = queue.subscribe(event_loop, filter_fn=bad_filter) + + queue.put(MockFfiEvent("audio_stream_event")) + event_loop.run_until_complete(asyncio.sleep(0.01)) + + # Item should be delivered despite filter error + received = [] + while not sub.empty(): + received.append(sub.get_nowait()) + + assert len(received) == 1 + + +class TestFfiQueueMemoryReduction: + """Test that filtering actually reduces object creation.""" + + @pytest.fixture + def event_loop(self) -> Generator[asyncio.AbstractEventLoop, None, None]: + loop = asyncio.new_event_loop() + yield loop + loop.close() + + def test_filtering_reduces_callback_calls(self, event_loop: asyncio.AbstractEventLoop) -> None: + """Verify filtering prevents call_soon_threadsafe for non-matching events.""" + queue: FfiQueue[MockFfiEvent] = FfiQueue() + + # Create 10 subscribers, each only wants audio events + subscribers = [] + for _ in range(10): + sub = queue.subscribe( + event_loop, + filter_fn=lambda e: e.WhichOneof("message") == "audio_stream_event", + ) + subscribers.append(sub) + + # Generate 1000 events, only 5% are audio + events = [] + for i in range(1000): + if i < 50: + events.append(MockFfiEvent("audio_stream_event")) + else: + events.append(MockFfiEvent("room_event")) + + for event in events: + queue.put(event) + + event_loop.run_until_complete(asyncio.sleep(0.1)) + + # Each subscriber should have received only 50 events (not 1000) + for sub in subscribers: + count = 0 + while not sub.empty(): + sub.get_nowait() + count += 1 + assert count == 50 diff --git a/tests/rtc/test_frame_metadata.py b/tests/rtc/test_frame_metadata.py new file mode 100644 index 00000000..31d53d76 --- /dev/null +++ b/tests/rtc/test_frame_metadata.py @@ -0,0 +1,179 @@ +from __future__ import annotations + +import weakref +from types import SimpleNamespace +from typing import cast + +import pytest + +from livekit import rtc +from livekit.rtc import video_source as video_source_module +from livekit.rtc._ffi_client import FfiHandle +from livekit.rtc._proto import e2ee_pb2 as proto_e2ee +from livekit.rtc._proto import ffi_pb2 as proto_ffi +from livekit.rtc._proto import handle_pb2 as proto_handle +from livekit.rtc._proto import participant_pb2 as proto_participant +from livekit.rtc._proto import room_pb2 as proto_room +from livekit.rtc._proto import track_pb2 as proto_track +from livekit.rtc.participant import LocalParticipant +from livekit.rtc.track import Track + + +def _publication_info( + sid: str, + *, + frame_metadata_features: list[proto_track.FrameMetadataFeature.ValueType] | None = None, +) -> proto_track.TrackPublicationInfo: + return proto_track.TrackPublicationInfo( + sid=sid, + name="camera", + kind=proto_track.KIND_VIDEO, + source=proto_track.SOURCE_CAMERA, + simulcasted=False, + width=640, + height=360, + mime_type="video/VP8", + muted=False, + remote=False, + encryption_type=proto_e2ee.NONE, + frame_metadata_features=frame_metadata_features or [], + ) + + +def _owned_publication( + sid: str, + *, + frame_metadata_features: list[proto_track.FrameMetadataFeature.ValueType] | None = None, +) -> proto_track.OwnedTrackPublication: + return proto_track.OwnedTrackPublication( + handle=proto_handle.FfiOwnedHandle(id=0), + info=_publication_info(sid, frame_metadata_features=frame_metadata_features), + ) + + +def test_frame_metadata_symbols_are_exported() -> None: + metadata = rtc.FrameMetadata(user_timestamp=123, frame_id=7) + + assert rtc.FrameMetadataFeature.FMF_USER_TIMESTAMP == proto_track.FMF_USER_TIMESTAMP + assert rtc.FrameMetadataFeature.FMF_FRAME_ID == proto_track.FMF_FRAME_ID + assert metadata.HasField("user_timestamp") + assert metadata.HasField("frame_id") + + +@pytest.mark.asyncio +async def test_track_publication_exposes_frame_metadata_features() -> None: + publication = rtc.LocalTrackPublication( + _owned_publication( + "TR_OLD", + frame_metadata_features=[ + proto_track.FMF_USER_TIMESTAMP, + proto_track.FMF_FRAME_ID, + ], + ) + ) + + assert publication.frame_metadata_features == [ + proto_track.FMF_USER_TIMESTAMP, + proto_track.FMF_FRAME_ID, + ] + + +def test_video_source_capture_frame_copies_metadata(monkeypatch: pytest.MonkeyPatch) -> None: + captured_requests: list[proto_ffi.FfiRequest] = [] + + class FakeClient: + def request(self, req: proto_ffi.FfiRequest) -> None: + captured_requests.append(req) + + class FakeFfiClient: + instance = FakeClient() + + monkeypatch.setattr(video_source_module, "FfiClient", FakeFfiClient) + + source = video_source_module.VideoSource.__new__(video_source_module.VideoSource) + source._ffi_handle = cast(FfiHandle, SimpleNamespace(handle=42)) + frame = rtc.VideoFrame( + width=1, + height=1, + type=rtc.VideoBufferType.RGBA, + data=bytes([0, 0, 0, 255]), + ) + + source.capture_frame( + frame, + timestamp_us=99, + rotation=rtc.VideoRotation.VIDEO_ROTATION_90, + metadata=rtc.FrameMetadata(user_timestamp=123, frame_id=7), + ) + + request = captured_requests[0] + assert request.capture_video_frame.source_handle == 42 + assert request.capture_video_frame.timestamp_us == 99 + assert request.capture_video_frame.rotation == rtc.VideoRotation.VIDEO_ROTATION_90 + assert request.capture_video_frame.HasField("metadata") + assert request.capture_video_frame.metadata.user_timestamp == 123 + assert request.capture_video_frame.metadata.frame_id == 7 + + +@pytest.mark.asyncio +async def test_local_track_republished_updates_existing_publication() -> None: + room = rtc.Room() + local_participant = LocalParticipant( + room._room_queue, + proto_participant.OwnedParticipant( + handle=proto_handle.FfiOwnedHandle(id=0), + info=proto_participant.ParticipantInfo( + sid="PA_LOCAL", + identity="publisher", + name="publisher", + state=proto_participant.PARTICIPANT_STATE_ACTIVE, + metadata="", + kind=proto_participant.PARTICIPANT_KIND_STANDARD, + disconnect_reason=proto_participant.UNKNOWN_REASON, + joined_at=0, + permission=proto_participant.ParticipantPermission(), + ), + ), + ) + room._local_participant = local_participant + + publication = rtc.LocalTrackPublication( + _owned_publication( + "TR_OLD", + frame_metadata_features=[proto_track.FMF_USER_TIMESTAMP], + ) + ) + # Build a real Track via __new__ (bypassing FFI) so the republish handler's + # track.sid invariant update and _set_room(...) re-push both work. + track = Track.__new__(Track) + track._info = proto_track.TrackInfo(sid="TR_OLD") + track._ffi_handle = cast(FfiHandle, None) + track._room_ref = None + track._audio_streams = weakref.WeakSet() + publication._track = track + local_participant._track_publications[publication.sid] = publication + + room._on_room_event( + proto_room.RoomEvent( + room_handle=0, + local_track_republished=proto_room.LocalTrackRepublished( + publication_handle=0, + previous_sid="TR_OLD", + info=_publication_info( + "TR_NEW", + frame_metadata_features=[ + proto_track.FMF_USER_TIMESTAMP, + proto_track.FMF_FRAME_ID, + ], + ), + ), + ) + ) + + assert "TR_OLD" not in local_participant.track_publications + assert local_participant.track_publications["TR_NEW"] is publication + assert publication.sid == "TR_NEW" + assert publication.frame_metadata_features == [ + proto_track.FMF_USER_TIMESTAMP, + proto_track.FMF_FRAME_ID, + ] diff --git a/tests/rtc/test_import.py b/tests/rtc/test_import.py new file mode 100644 index 00000000..2728f93e --- /dev/null +++ b/tests/rtc/test_import.py @@ -0,0 +1,9 @@ +"""Smoke test: import the SDK and initialize the FFI library.""" + + +def test_import_and_ffi_initialize() -> None: + from livekit import rtc # noqa: F401 + from livekit.rtc._ffi_client import FfiClient + + # accessing .instance triggers livekit_ffi_initialize + assert FfiClient.instance is not None diff --git a/tests/rtc/test_mixer.py b/tests/rtc/test_mixer.py new file mode 100644 index 00000000..1ab75f4b --- /dev/null +++ b/tests/rtc/test_mixer.py @@ -0,0 +1,58 @@ +# type: ignore + +import numpy as np +import pytest + +from livekit.rtc import AudioMixer +from livekit.rtc.utils import sine_wave_generator + +SAMPLE_RATE = 48000 +BLOCKSIZE = SAMPLE_RATE // 10 + + +@pytest.mark.asyncio +async def test_mixer_two_sine_waves(): + """ + Test that mixing two sine waves (440Hz and 880Hz) produces an output + containing both frequency components. + """ + duration = 1.0 + mixer = AudioMixer( + sample_rate=SAMPLE_RATE, + num_channels=1, + blocksize=BLOCKSIZE, + stream_timeout_ms=100, + capacity=100, + ) + stream1 = sine_wave_generator(440, duration, SAMPLE_RATE) + stream2 = sine_wave_generator(880, duration, SAMPLE_RATE) + mixer.add_stream(stream1) + mixer.add_stream(stream2) + mixer.end_input() + + mixed_signals = [] + async for frame in mixer: + data = np.frombuffer(frame.data.tobytes(), dtype=np.int16) + mixed_signals.append(data) + + await mixer.aclose() + + if not mixed_signals: + pytest.fail("No frames were produced by the mixer.") + + mixed_signal = np.concatenate(mixed_signals) + + # Use FFT to analyze frequency components. + fft = np.fft.rfft(mixed_signal) + freqs = np.fft.rfftfreq(len(mixed_signal), 1 / SAMPLE_RATE) + magnitude = np.abs(fft) + + # Identify peak frequencies. We'll pick the 5 highest peaks. + peak_indices = np.argsort(magnitude)[-5:] + peak_freqs = freqs[peak_indices] + + print("Peak frequencies:", peak_freqs) + + # Assert that the peaks include 440Hz and 880Hz (with a tolerance of ±5 Hz) + assert any(np.isclose(peak_freqs, 440, atol=5)), f"Expected 440 Hz in peaks, got: {peak_freqs}" + assert any(np.isclose(peak_freqs, 880, atol=5)), f"Expected 880 Hz in peaks, got: {peak_freqs}" diff --git a/tests/rtc/test_platform_audio.py b/tests/rtc/test_platform_audio.py new file mode 100644 index 00000000..0742d28f --- /dev/null +++ b/tests/rtc/test_platform_audio.py @@ -0,0 +1,332 @@ +""" +Tests for PlatformAudio functionality. + +These tests require audio hardware to be available. On CI environments without +audio devices, tests will be skipped automatically. +""" + +import pytest +from typing import Optional + +from livekit import rtc + + +# Global to cache PlatformAudio availability check +_platform_audio_available: Optional[bool] = None +_platform_audio_error: Optional[str] = None + + +def _dispose_track(track: rtc.Track) -> None: + """Release track handles created only for local PlatformAudio tests. + + Note: Track doesn't have a public close() method yet, so we use the internal API. + """ + track._ffi_handle.dispose() + + +def _check_platform_audio_available() -> tuple[bool, Optional[str]]: + """Check if PlatformAudio can be initialized on this system.""" + global _platform_audio_available, _platform_audio_error + + if _platform_audio_available is not None: + return _platform_audio_available, _platform_audio_error + + try: + pa = rtc.PlatformAudio() + _platform_audio_available = True + _platform_audio_error = None + pa.close() + except rtc.PlatformAudioError as e: + _platform_audio_available = False + _platform_audio_error = str(e) + + return _platform_audio_available, _platform_audio_error + + +def requires_platform_audio(func): + """Decorator to skip tests if PlatformAudio is not available.""" + + @pytest.mark.skipif( + not _check_platform_audio_available()[0], + reason=f"PlatformAudio not available: {_check_platform_audio_available()[1]}", + ) + def wrapper(*args, **kwargs): + return func(*args, **kwargs) + + wrapper.__name__ = func.__name__ + wrapper.__doc__ = func.__doc__ + return wrapper + + +# Use a module-scoped fixture to avoid creating multiple PlatformAudio instances +@pytest.fixture(scope="module") +def platform_audio(): + """Create a PlatformAudio instance for testing.""" + available, error = _check_platform_audio_available() + if not available: + pytest.skip(f"PlatformAudio not available: {error}") + + pa = rtc.PlatformAudio() + try: + yield pa + finally: + pa.close() + + +class TestPlatformAudioCreation: + """Tests for PlatformAudio initialization.""" + + def test_platform_audio_creation(self, platform_audio): + """Test that PlatformAudio can be created successfully.""" + assert platform_audio is not None + + def test_platform_audio_multiple_instances(self, platform_audio): + """Test that multiple PlatformAudio instances can coexist.""" + # Creating another instance should reuse the same underlying ADM + available, error = _check_platform_audio_available() + if not available: + pytest.skip(f"PlatformAudio not available: {error}") + + pa2 = rtc.PlatformAudio() + try: + assert pa2 is not None + # Both should work + assert platform_audio is not None + finally: + pa2.close() + + +class TestDeviceEnumeration: + """Tests for audio device enumeration.""" + + def test_recording_devices_returns_list(self, platform_audio): + """Test that recording_devices() returns a list.""" + devices = platform_audio.recording_devices() + assert isinstance(devices, list) + + def test_playout_devices_returns_list(self, platform_audio): + """Test that playout_devices() returns a list.""" + devices = platform_audio.playout_devices() + assert isinstance(devices, list) + + def test_recording_device_info_structure(self, platform_audio): + """Test that recording devices have correct structure.""" + devices = platform_audio.recording_devices() + if not devices: + pytest.skip("No recording devices available") + + device = devices[0] + assert isinstance(device, rtc.AudioDeviceInfo) + assert isinstance(device.index, int) + assert isinstance(device.name, str) + assert isinstance(device.id, str) + assert device.index >= 0 + assert len(device.name) > 0 + + def test_playout_device_info_structure(self, platform_audio): + """Test that playout devices have correct structure.""" + devices = platform_audio.playout_devices() + if not devices: + pytest.skip("No playout devices available") + + device = devices[0] + assert isinstance(device, rtc.AudioDeviceInfo) + assert isinstance(device.index, int) + assert isinstance(device.name, str) + assert isinstance(device.id, str) + assert device.index >= 0 + assert len(device.name) > 0 + + def test_device_indices_are_sequential(self, platform_audio): + """Test that device indices start at 0 and are sequential.""" + recording = platform_audio.recording_devices() + playout = platform_audio.playout_devices() + + for i, device in enumerate(recording): + assert device.index == i, f"Recording device index mismatch: {device.index} != {i}" + + for i, device in enumerate(playout): + assert device.index == i, f"Playout device index mismatch: {device.index} != {i}" + + +class TestDeviceSelection: + """Tests for audio device selection.""" + + def test_set_recording_device_valid(self, platform_audio): + """Test setting a valid recording device.""" + devices = platform_audio.recording_devices() + if not devices: + pytest.skip("No recording devices available") + + # Should not raise + platform_audio.set_recording_device(devices[0].id) + + def test_set_playout_device_valid(self, platform_audio): + """Test setting a valid playout device.""" + devices = platform_audio.playout_devices() + if not devices: + pytest.skip("No playout devices available") + + # Should not raise + platform_audio.set_playout_device(devices[0].id) + + def test_set_recording_device_invalid_platform_specific(self, platform_audio): + """Test behavior when setting an invalid recording device. + + Platform-specific behavior: + - macOS: Silently falls back to default device (no error) + - Windows: Raises PlatformAudioError with "Device not found" + + Both behaviors are valid - the test accepts either outcome. + """ + try: + platform_audio.set_recording_device("invalid-device-id-that-does-not-exist") + # macOS: falls back to default device silently + except rtc.PlatformAudioError as e: + # Windows: raises error for invalid device + assert "not found" in str(e).lower() or "failed" in str(e).lower() + + def test_set_playout_device_invalid_platform_specific(self, platform_audio): + """Test behavior when setting an invalid playout device. + + Platform-specific behavior: + - macOS: Silently falls back to default device (no error) + - Windows: Raises PlatformAudioError with "Device not found" + + Both behaviors are valid - the test accepts either outcome. + """ + try: + platform_audio.set_playout_device("invalid-device-id-that-does-not-exist") + # macOS: falls back to default device silently + except rtc.PlatformAudioError as e: + # Windows: raises error for invalid device + assert "not found" in str(e).lower() or "failed" in str(e).lower() + + +class TestAudioSourceCreation: + """Tests for PlatformAudioSource creation.""" + + def test_create_audio_source_default_options(self, platform_audio): + """Test creating an audio source with default options.""" + source = platform_audio.create_audio_source() + try: + assert source is not None + assert isinstance(source, rtc.PlatformAudioSource) + finally: + source.close() + + def test_create_audio_source_custom_options(self, platform_audio): + """Test creating an audio source with custom options.""" + options = rtc.PlatformAudioOptions( + echo_cancellation=True, + noise_suppression=True, + auto_gain_control=True, + prefer_hardware=False, + ) + source = platform_audio.create_audio_source(options) + try: + assert source is not None + assert isinstance(source, rtc.PlatformAudioSource) + finally: + source.close() + + def test_create_audio_source_all_processing_disabled(self, platform_audio): + """Test creating an audio source with all processing disabled.""" + options = rtc.PlatformAudioOptions( + echo_cancellation=False, + noise_suppression=False, + auto_gain_control=False, + ) + source = platform_audio.create_audio_source(options) + try: + assert source is not None + finally: + source.close() + + def test_audio_source_has_handle(self, platform_audio): + """Test that created audio source has a valid internal handle.""" + source = platform_audio.create_audio_source() + try: + # The _handle property is used internally for track creation + assert hasattr(source, "_handle") + assert source._handle > 0 + finally: + source.close() + + def test_create_multiple_audio_sources(self, platform_audio): + """Test creating multiple audio sources from the same PlatformAudio.""" + source1 = platform_audio.create_audio_source() + source2 = None + try: + source2 = platform_audio.create_audio_source() + assert source1 is not None + assert source2 is not None + # Each source should have a unique handle + assert source1._handle != source2._handle + finally: + source1.close() + if source2 is not None: + source2.close() + + +class TestPlatformAudioOptions: + """Tests for PlatformAudioOptions dataclass.""" + + def test_default_options(self): + """Test default PlatformAudioOptions values.""" + options = rtc.PlatformAudioOptions() + assert options.echo_cancellation is True + assert options.noise_suppression is True + assert options.auto_gain_control is True + assert options.prefer_hardware is False + + def test_custom_options(self): + """Test custom PlatformAudioOptions values.""" + options = rtc.PlatformAudioOptions( + echo_cancellation=False, + noise_suppression=False, + auto_gain_control=False, + prefer_hardware=True, + ) + assert options.echo_cancellation is False + assert options.noise_suppression is False + assert options.auto_gain_control is False + assert options.prefer_hardware is True + + +class TestAudioDeviceInfo: + """Tests for AudioDeviceInfo dataclass.""" + + def test_audio_device_info_creation(self): + """Test creating AudioDeviceInfo manually.""" + info = rtc.AudioDeviceInfo(index=0, name="Test Mic", id="test-guid-123") + assert info.index == 0 + assert info.name == "Test Mic" + assert info.id == "test-guid-123" + + def test_audio_device_info_equality(self): + """Test AudioDeviceInfo equality comparison.""" + info1 = rtc.AudioDeviceInfo(index=0, name="Test Mic", id="guid-1") + info2 = rtc.AudioDeviceInfo(index=0, name="Test Mic", id="guid-1") + info3 = rtc.AudioDeviceInfo(index=1, name="Other Mic", id="guid-2") + + assert info1 == info2 + assert info1 != info3 + + +class TestIntegrationWithTrack: + """Integration tests with LocalAudioTrack.""" + + def test_create_track_from_platform_audio_source(self, platform_audio): + """Test creating a LocalAudioTrack from PlatformAudioSource.""" + source = platform_audio.create_audio_source() + track = None + try: + track = rtc.LocalAudioTrack.create_audio_track("test-mic", source) + assert track is not None + assert track.name == "test-mic" + assert track.kind == rtc.TrackKind.KIND_AUDIO + finally: + if track is not None: + _dispose_track(track) + source.close() diff --git a/tests/rtc/test_resampler.py b/tests/rtc/test_resampler.py new file mode 100644 index 00000000..db05c4fd --- /dev/null +++ b/tests/rtc/test_resampler.py @@ -0,0 +1,67 @@ +from livekit.rtc import AudioResampler, AudioResamplerQuality, AudioFrame +import time +import wave +import os +from typing import List + +# Test fixture directory +FIXTURES_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures") + + +def test_audio_resampler() -> None: + wav_file_path = os.path.join(FIXTURES_DIR, "test_audio.wav") + + # Open the wave file + with wave.open(wav_file_path, "rb") as wf_in: + n_channels = wf_in.getnchannels() + sampwidth = wf_in.getsampwidth() + n_frames = wf_in.getnframes() + audio_data = bytearray(wf_in.readframes(n_frames)) + + if sampwidth != 2: + raise ValueError(f"Expected 16-bit PCM data, but got {sampwidth * 8}-bit.") + + qualities = [ + AudioResamplerQuality.QUICK, + AudioResamplerQuality.LOW, + AudioResamplerQuality.MEDIUM, + AudioResamplerQuality.HIGH, + AudioResamplerQuality.VERY_HIGH, + ] + + for quality in qualities: + total_time = 0.0 + nb_runs = 20 + output_frames: List[AudioFrame] = [] + for i in range(nb_runs): + output_frames = [] + resampler = AudioResampler(44100, 8000, quality=quality) + + start_time = time.perf_counter() + + chunk_size = 1024 * n_channels * sampwidth + output_frames = [] + for i in range(0, len(audio_data), chunk_size): + chunk = audio_data[i : i + chunk_size] + frames = resampler.push(bytearray(chunk)) + output_frames.extend(frames) + + frames = resampler.flush() + output_frames.extend(frames) + + end_time = time.perf_counter() + total_time += end_time - start_time + + total_time = total_time * 1000 + print(f"Quality: {quality}, Average time: {total_time / nb_runs:.2f}ms") + + output_data = b"" + + for frame in output_frames: + output_data += frame.data + + with wave.open(f"audio_resampled_{quality.name}.wav", "wb") as wf_out: + wf_out.setnchannels(n_channels) + wf_out.setsampwidth(sampwidth) + wf_out.setframerate(8000) + wf_out.writeframes(output_data) diff --git a/uv.lock b/uv.lock new file mode 100644 index 00000000..e912b879 --- /dev/null +++ b/uv.lock @@ -0,0 +1,3914 @@ +version = 1 +revision = 3 +requires-python = ">=3.9" +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] + +[manifest] +members = [ + "livekit", + "livekit-api", + "livekit-protocol", + "python-sdks", +] + +[[package]] +name = "aiofiles" +version = "25.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/41/c3/534eac40372d8ee36ef40df62ec129bee4fdb5ad9706e58a29be53b2c970/aiofiles-25.1.0.tar.gz", hash = "sha256:a8d728f0a29de45dc521f18f07297428d56992a742f0cd2701ba86e44d23d5b2", size = 46354, upload-time = "2025-10-09T20:51:04.358Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bc/8a/340a1555ae33d7354dbca4faa54948d76d89a27ceef032c8c3bc661d003e/aiofiles-25.1.0-py3-none-any.whl", hash = "sha256:abe311e527c862958650f9438e859c1fa7568a141b22abcd015e120e86a85695", size = 14668, upload-time = "2025-10-09T20:51:03.174Z" }, +] + +[[package]] +name = "aiohappyeyeballs" +version = "2.6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/26/30/f84a107a9c4331c14b2b586036f40965c128aa4fee4dda5d3d51cb14ad54/aiohappyeyeballs-2.6.1.tar.gz", hash = "sha256:c3f9d0113123803ccadfdf3f0faa505bc78e6a72d1cc4806cbd719826e943558", size = 22760, upload-time = "2025-03-12T01:42:48.764Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0f/15/5bf3b99495fb160b63f95972b81750f18f7f4e02ad051373b669d17d44f2/aiohappyeyeballs-2.6.1-py3-none-any.whl", hash = "sha256:f349ba8f4b75cb25c99c5c2d84e997e485204d2902a9597802b0371f09331fb8", size = 15265, upload-time = "2025-03-12T01:42:47.083Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.13.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "aiohappyeyeballs", marker = "python_full_version < '3.10'" }, + { name = "aiosignal", marker = "python_full_version < '3.10'" }, + { name = "async-timeout", marker = "python_full_version < '3.10'" }, + { name = "attrs", marker = "python_full_version < '3.10'" }, + { name = "frozenlist", marker = "python_full_version < '3.10'" }, + { name = "multidict", marker = "python_full_version < '3.10'" }, + { name = "propcache", marker = "python_full_version < '3.10'" }, + { name = "yarl", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/77/9a/152096d4808df8e4268befa55fba462f440f14beab85e8ad9bf990516918/aiohttp-3.13.5.tar.gz", hash = "sha256:9d98cc980ecc96be6eb4c1994ce35d28d8b1f5e5208a23b421187d1209dbb7d1", size = 7858271, upload-time = "2026-03-31T22:01:03.343Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/85/cebc47ee74d8b408749073a1a46c6fcba13d170dc8af7e61996c6c9394ac/aiohttp-3.13.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:02222e7e233295f40e011c1b00e3b0bd451f22cf853a0304c3595633ee47da4b", size = 750547, upload-time = "2026-03-31T21:56:30.024Z" }, + { url = "https://files.pythonhosted.org/packages/05/98/afd308e35b9d3d8c9ec54c0918f1d722c86dc17ddfec272fcdbcce5a3124/aiohttp-3.13.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bace460460ed20614fa6bc8cb09966c0b8517b8c58ad8046828c6078d25333b5", size = 503535, upload-time = "2026-03-31T21:56:31.935Z" }, + { url = "https://files.pythonhosted.org/packages/6f/4d/926c183e06b09d5270a309eb50fbde7b09782bfd305dec1e800f329834fb/aiohttp-3.13.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8f546a4dc1e6a5edbb9fd1fd6ad18134550e096a5a43f4ad74acfbd834fc6670", size = 497830, upload-time = "2026-03-31T21:56:33.654Z" }, + { url = "https://files.pythonhosted.org/packages/e4/d6/f47d1c690f115a5c2a5e8938cce4a232a5be9aac5c5fb2647efcbbbda333/aiohttp-3.13.5-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c86969d012e51b8e415a8c6ce96f7857d6a87d6207303ab02d5d11ef0cad2274", size = 1682474, upload-time = "2026-03-31T21:56:35.513Z" }, + { url = "https://files.pythonhosted.org/packages/01/44/056fd37b1bb52eac760303e5196acc74d9d546631b035704ae5927f7b4ac/aiohttp-3.13.5-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b6f6cd1560c5fa427e3b6074bb24d2c64e225afbb7165008903bd42e4e33e28a", size = 1655259, upload-time = "2026-03-31T21:56:37.843Z" }, + { url = "https://files.pythonhosted.org/packages/91/9f/78eb1a20c1c28ae02f6a3c0f4d7b0dcc66abce5290cadd53d78ce3084175/aiohttp-3.13.5-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:636bc362f0c5bbc7372bc3ae49737f9e3030dbce469f0f422c8f38079780363d", size = 1736204, upload-time = "2026-03-31T21:56:39.822Z" }, + { url = "https://files.pythonhosted.org/packages/de/6c/d20d7de23f0b52b8c1d9e2033b2db1ac4dacbb470bb74c56de0f5f86bb4f/aiohttp-3.13.5-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6a7cbeb06d1070f1d14895eeeed4dac5913b22d7b456f2eb969f11f4b3993796", size = 1826198, upload-time = "2026-03-31T21:56:41.378Z" }, + { url = "https://files.pythonhosted.org/packages/2f/86/a6f3ff1fd795f49545a7c74b2c92f62729135d73e7e4055bf74da5a26c82/aiohttp-3.13.5-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca9ef7517fd7874a1a08970ae88f497bf5c984610caa0bf40bd7e8450852b95", size = 1681329, upload-time = "2026-03-31T21:56:43.374Z" }, + { url = "https://files.pythonhosted.org/packages/fb/68/84cd3dab6b7b4f3e6fe9459a961acb142aaab846417f6e8905110d7027e5/aiohttp-3.13.5-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:019a67772e034a0e6b9b17c13d0a8fe56ad9fb150fc724b7f3ffd3724288d9e5", size = 1560023, upload-time = "2026-03-31T21:56:45.031Z" }, + { url = "https://files.pythonhosted.org/packages/41/2c/db61b64b0249e30f954a65ab4cb4970ced57544b1de2e3c98ee5dc24165f/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:f34ecee82858e41dd217734f0c41a532bd066bcaab636ad830f03a30b2a96f2a", size = 1652372, upload-time = "2026-03-31T21:56:47.075Z" }, + { url = "https://files.pythonhosted.org/packages/25/6f/e96988a6c982d047810c772e28c43c64c300c943b0ed5c1c0c4ce1e1027c/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:4eac02d9af4813ee289cd63a361576da36dba57f5a1ab36377bc2600db0cbb73", size = 1662031, upload-time = "2026-03-31T21:56:48.835Z" }, + { url = "https://files.pythonhosted.org/packages/b7/26/a56feace81f3d347b4052403a9d03754a0ab23f7940780dada0849a38c92/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:4beac52e9fe46d6abf98b0176a88154b742e878fdf209d2248e99fcdf73cd297", size = 1708118, upload-time = "2026-03-31T21:56:50.833Z" }, + { url = "https://files.pythonhosted.org/packages/78/6e/b6173a8ff03d01d5e1a694bc06764b5dad1df2d4ed8f0ceec12bb3277936/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:c180f480207a9b2475f2b8d8bd7204e47aec952d084b2a2be58a782ffcf96074", size = 1548667, upload-time = "2026-03-31T21:56:52.81Z" }, + { url = "https://files.pythonhosted.org/packages/16/13/13296ffe2c132d888b3fe2c195c8b9c0c24c89c3fa5cc2c44464dc23b22e/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2837fb92951564d6339cedae4a7231692aa9f73cbc4fb2e04263b96844e03b4e", size = 1724490, upload-time = "2026-03-31T21:56:54.541Z" }, + { url = "https://files.pythonhosted.org/packages/7a/b4/1f1c287f4a79782ef36e5a6e62954c85343bc30470d862d30bd5f26c9fa2/aiohttp-3.13.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:d9010032a0b9710f58012a1e9c222528763d860ba2ee1422c03473eab47703e7", size = 1667109, upload-time = "2026-03-31T21:56:56.21Z" }, + { url = "https://files.pythonhosted.org/packages/ef/42/8461a2aaf60a8f4ea4549a4056be36b904b0eb03d97ca9a8a2604681a500/aiohttp-3.13.5-cp310-cp310-win32.whl", hash = "sha256:7c4b6668b2b2b9027f209ddf647f2a4407784b5d88b8be4efcc72036f365baf9", size = 439478, upload-time = "2026-03-31T21:56:58.292Z" }, + { url = "https://files.pythonhosted.org/packages/e5/71/06956304cb5ee439dfe8d86e1b2e70088bd88ed1ced1f42fb29e5d855f0e/aiohttp-3.13.5-cp310-cp310-win_amd64.whl", hash = "sha256:cd3db5927bf9167d5a6157ddb2f036f6b6b0ad001ac82355d43e97a4bde76d76", size = 462047, upload-time = "2026-03-31T21:57:00.257Z" }, + { url = "https://files.pythonhosted.org/packages/d6/f5/a20c4ac64aeaef1679e25c9983573618ff765d7aa829fa2b84ae7573169e/aiohttp-3.13.5-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ab7229b6f9b5c1ba4910d6c41a9eb11f543eadb3f384df1b4c293f4e73d44d6", size = 757513, upload-time = "2026-03-31T21:57:02.146Z" }, + { url = "https://files.pythonhosted.org/packages/75/0a/39fa6c6b179b53fcb3e4b3d2b6d6cad0180854eda17060c7218540102bef/aiohttp-3.13.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f14c50708bb156b3a3ca7230b3d820199d56a48e3af76fa21c2d6087190fe3d", size = 506748, upload-time = "2026-03-31T21:57:04.275Z" }, + { url = "https://files.pythonhosted.org/packages/87/ec/e38ce072e724fd7add6243613f8d1810da084f54175353d25ccf9f9c7e5a/aiohttp-3.13.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e7d2f8616f0ff60bd332022279011776c3ac0faa0f1b463f7bb12326fbc97a1c", size = 501673, upload-time = "2026-03-31T21:57:06.208Z" }, + { url = "https://files.pythonhosted.org/packages/ba/ba/3bc7525d7e2beaa11b309a70d48b0d3cfc3c2089ec6a7d0820d59c657053/aiohttp-3.13.5-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2567b72e1ffc3ab25510db43f355b29eeada56c0a622e58dcdb19530eb0a3cb", size = 1763757, upload-time = "2026-03-31T21:57:07.882Z" }, + { url = "https://files.pythonhosted.org/packages/5e/ab/e87744cf18f1bd78263aba24924d4953b41086bd3a31d22452378e9028a0/aiohttp-3.13.5-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fb0540c854ac9c0c5ad495908fdfd3e332d553ec731698c0e29b1877ba0d2ec6", size = 1720152, upload-time = "2026-03-31T21:57:09.946Z" }, + { url = "https://files.pythonhosted.org/packages/6b/f3/ed17a6f2d742af17b50bae2d152315ed1b164b07a5fd5cc1754d99e4dfa5/aiohttp-3.13.5-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c9883051c6972f58bfc4ebb2116345ee2aa151178e99c3f2b2bbe2af712abd13", size = 1818010, upload-time = "2026-03-31T21:57:12.157Z" }, + { url = "https://files.pythonhosted.org/packages/53/06/ecbc63dc937192e2a5cb46df4d3edb21deb8225535818802f210a6ea5816/aiohttp-3.13.5-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2294172ce08a82fb7c7273485895de1fa1186cc8294cfeb6aef4af42ad261174", size = 1907251, upload-time = "2026-03-31T21:57:14.023Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a5/0521aa32c1ddf3aa1e71dcc466be0b7db2771907a13f18cddaa45967d97b/aiohttp-3.13.5-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3a807cabd5115fb55af198b98178997a5e0e57dead43eb74a93d9c07d6d4a7dc", size = 1759969, upload-time = "2026-03-31T21:57:16.146Z" }, + { url = "https://files.pythonhosted.org/packages/f6/78/a38f8c9105199dd3b9706745865a8a59d0041b6be0ca0cc4b2ccf1bab374/aiohttp-3.13.5-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:aa6d0d932e0f39c02b80744273cd5c388a2d9bc07760a03164f229c8e02662f6", size = 1616871, upload-time = "2026-03-31T21:57:17.856Z" }, + { url = "https://files.pythonhosted.org/packages/6f/41/27392a61ead8ab38072105c71aa44ff891e71653fe53d576a7067da2b4e8/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:60869c7ac4aaabe7110f26499f3e6e5696eae98144735b12a9c3d9eae2b51a49", size = 1739844, upload-time = "2026-03-31T21:57:19.679Z" }, + { url = "https://files.pythonhosted.org/packages/6e/55/5564e7ae26d94f3214250009a0b1c65a0c6af4bf88924ccb6fdab901de28/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:26d2f8546f1dfa75efa50c3488215a903c0168d253b75fba4210f57ab77a0fb8", size = 1731969, upload-time = "2026-03-31T21:57:22.006Z" }, + { url = "https://files.pythonhosted.org/packages/6d/c5/705a3929149865fc941bcbdd1047b238e4a72bcb215a9b16b9d7a2e8d992/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f1162a1492032c82f14271e831c8f4b49f2b6078f4f5fc74de2c912fa225d51d", size = 1795193, upload-time = "2026-03-31T21:57:24.256Z" }, + { url = "https://files.pythonhosted.org/packages/a6/19/edabed62f718d02cff7231ca0db4ef1c72504235bc467f7b67adb1679f48/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:8b14eb3262fad0dc2f89c1a43b13727e709504972186ff6a99a3ecaa77102b6c", size = 1606477, upload-time = "2026-03-31T21:57:26.364Z" }, + { url = "https://files.pythonhosted.org/packages/de/fc/76f80ef008675637d88d0b21584596dc27410a990b0918cb1e5776545b5b/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:ca9ac61ac6db4eb6c2a0cd1d0f7e1357647b638ccc92f7e9d8d133e71ed3c6ac", size = 1813198, upload-time = "2026-03-31T21:57:28.316Z" }, + { url = "https://files.pythonhosted.org/packages/e5/67/5b3ac26b80adb20ea541c487f73730dc8fa107d632c998f25bbbab98fcda/aiohttp-3.13.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7996023b2ed59489ae4762256c8516df9820f751cf2c5da8ed2fb20ee50abab3", size = 1752321, upload-time = "2026-03-31T21:57:30.549Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/e4a2e49255ea23fa4feeb5ab092d90240d927c15e47b5b5c48dff5a9ce29/aiohttp-3.13.5-cp311-cp311-win32.whl", hash = "sha256:77dfa48c9f8013271011e51c00f8ada19851f013cde2c48fca1ba5e0caf5bb06", size = 439069, upload-time = "2026-03-31T21:57:32.388Z" }, + { url = "https://files.pythonhosted.org/packages/c0/43/8c7163a596dab4f8be12c190cf467a1e07e4734cf90eebb39f7f5d53fc6a/aiohttp-3.13.5-cp311-cp311-win_amd64.whl", hash = "sha256:d3a4834f221061624b8887090637db9ad4f61752001eae37d56c52fddade2dc8", size = 462859, upload-time = "2026-03-31T21:57:34.455Z" }, + { url = "https://files.pythonhosted.org/packages/be/6f/353954c29e7dcce7cf00280a02c75f30e133c00793c7a2ed3776d7b2f426/aiohttp-3.13.5-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:023ecba036ddd840b0b19bf195bfae970083fd7024ce1ac22e9bba90464620e9", size = 748876, upload-time = "2026-03-31T21:57:36.319Z" }, + { url = "https://files.pythonhosted.org/packages/f5/1b/428a7c64687b3b2e9cd293186695affc0e1e54a445d0361743b231f11066/aiohttp-3.13.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:15c933ad7920b7d9a20de151efcd05a6e38302cbf0e10c9b2acb9a42210a2416", size = 499557, upload-time = "2026-03-31T21:57:38.236Z" }, + { url = "https://files.pythonhosted.org/packages/29/47/7be41556bfbb6917069d6a6634bb7dd5e163ba445b783a90d40f5ac7e3a7/aiohttp-3.13.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ab2899f9fa2f9f741896ebb6fa07c4c883bfa5c7f2ddd8cf2aafa86fa981b2d2", size = 500258, upload-time = "2026-03-31T21:57:39.923Z" }, + { url = "https://files.pythonhosted.org/packages/67/84/c9ecc5828cb0b3695856c07c0a6817a99d51e2473400f705275a2b3d9239/aiohttp-3.13.5-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a60eaa2d440cd4707696b52e40ed3e2b0f73f65be07fd0ef23b6b539c9c0b0b4", size = 1749199, upload-time = "2026-03-31T21:57:41.938Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d3/3c6d610e66b495657622edb6ae7c7fd31b2e9086b4ec50b47897ad6042a9/aiohttp-3.13.5-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:55b3bdd3292283295774ab585160c4004f4f2f203946997f49aac032c84649e9", size = 1721013, upload-time = "2026-03-31T21:57:43.904Z" }, + { url = "https://files.pythonhosted.org/packages/49/a0/24409c12217456df0bae7babe3b014e460b0b38a8e60753d6cb339f6556d/aiohttp-3.13.5-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c2b2355dc094e5f7d45a7bb262fe7207aa0460b37a0d87027dcf21b5d890e7d5", size = 1781501, upload-time = "2026-03-31T21:57:46.285Z" }, + { url = "https://files.pythonhosted.org/packages/98/9d/b65ec649adc5bccc008b0957a9a9c691070aeac4e41cea18559fef49958b/aiohttp-3.13.5-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b38765950832f7d728297689ad78f5f2cf79ff82487131c4d26fe6ceecdc5f8e", size = 1878981, upload-time = "2026-03-31T21:57:48.734Z" }, + { url = "https://files.pythonhosted.org/packages/57/d8/8d44036d7eb7b6a8ec4c5494ea0c8c8b94fbc0ed3991c1a7adf230df03bf/aiohttp-3.13.5-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b18f31b80d5a33661e08c89e202edabf1986e9b49c42b4504371daeaa11b47c1", size = 1767934, upload-time = "2026-03-31T21:57:51.171Z" }, + { url = "https://files.pythonhosted.org/packages/31/04/d3f8211f273356f158e3464e9e45484d3fb8c4ce5eb2f6fe9405c3273983/aiohttp-3.13.5-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:33add2463dde55c4f2d9635c6ab33ce154e5ecf322bd26d09af95c5f81cfa286", size = 1566671, upload-time = "2026-03-31T21:57:53.326Z" }, + { url = "https://files.pythonhosted.org/packages/41/db/073e4ebe00b78e2dfcacff734291651729a62953b48933d765dc513bf798/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:327cc432fdf1356fb4fbc6fe833ad4e9f6aacb71a8acaa5f1855e4b25910e4a9", size = 1705219, upload-time = "2026-03-31T21:57:55.385Z" }, + { url = "https://files.pythonhosted.org/packages/48/45/7dfba71a2f9fd97b15c95c06819de7eb38113d2cdb6319669195a7d64270/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:7c35b0bf0b48a70b4cb4fc5d7bed9b932532728e124874355de1a0af8ec4bc88", size = 1743049, upload-time = "2026-03-31T21:57:57.341Z" }, + { url = "https://files.pythonhosted.org/packages/18/71/901db0061e0f717d226386a7f471bb59b19566f2cae5f0d93874b017271f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:df23d57718f24badef8656c49743e11a89fd6f5358fa8a7b96e728fda2abf7d3", size = 1749557, upload-time = "2026-03-31T21:57:59.626Z" }, + { url = "https://files.pythonhosted.org/packages/08/d5/41eebd16066e59cd43728fe74bce953d7402f2b4ddfdfef2c0e9f17ca274/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:02e048037a6501a5ec1f6fc9736135aec6eb8a004ce48838cb951c515f32c80b", size = 1558931, upload-time = "2026-03-31T21:58:01.972Z" }, + { url = "https://files.pythonhosted.org/packages/30/e6/4a799798bf05740e66c3a1161079bda7a3dd8e22ca392481d7a7f9af82a6/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31cebae8b26f8a615d2b546fee45d5ffb76852ae6450e2a03f42c9102260d6fe", size = 1774125, upload-time = "2026-03-31T21:58:04.007Z" }, + { url = "https://files.pythonhosted.org/packages/84/63/7749337c90f92bc2cb18f9560d67aa6258c7060d1397d21529b8004fcf6f/aiohttp-3.13.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:888e78eb5ca55a615d285c3c09a7a91b42e9dd6fc699b166ebd5dee87c9ccf14", size = 1732427, upload-time = "2026-03-31T21:58:06.337Z" }, + { url = "https://files.pythonhosted.org/packages/98/de/cf2f44ff98d307e72fb97d5f5bbae3bfcb442f0ea9790c0bf5c5c2331404/aiohttp-3.13.5-cp312-cp312-win32.whl", hash = "sha256:8bd3ec6376e68a41f9f95f5ed170e2fcf22d4eb27a1f8cb361d0508f6e0557f3", size = 433534, upload-time = "2026-03-31T21:58:08.712Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ca/eadf6f9c8fa5e31d40993e3db153fb5ed0b11008ad5d9de98a95045bed84/aiohttp-3.13.5-cp312-cp312-win_amd64.whl", hash = "sha256:110e448e02c729bcebb18c60b9214a87ba33bac4a9fa5e9a5f139938b56c6cb1", size = 460446, upload-time = "2026-03-31T21:58:10.945Z" }, + { url = "https://files.pythonhosted.org/packages/78/e9/d76bf503005709e390122d34e15256b88f7008e246c4bdbe915cd4f1adce/aiohttp-3.13.5-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5029cc80718bbd545123cd8fe5d15025eccaaaace5d0eeec6bd556ad6163d61", size = 742930, upload-time = "2026-03-31T21:58:13.155Z" }, + { url = "https://files.pythonhosted.org/packages/57/00/4b7b70223deaebd9bb85984d01a764b0d7bd6526fcdc73cca83bcbe7243e/aiohttp-3.13.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4bb6bf5811620003614076bdc807ef3b5e38244f9d25ca5fe888eaccea2a9832", size = 496927, upload-time = "2026-03-31T21:58:15.073Z" }, + { url = "https://files.pythonhosted.org/packages/9c/f5/0fb20fb49f8efdcdce6cd8127604ad2c503e754a8f139f5e02b01626523f/aiohttp-3.13.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a84792f8631bf5a94e52d9cc881c0b824ab42717165a5579c760b830d9392ac9", size = 497141, upload-time = "2026-03-31T21:58:17.009Z" }, + { url = "https://files.pythonhosted.org/packages/3b/86/b7c870053e36a94e8951b803cb5b909bfbc9b90ca941527f5fcafbf6b0fa/aiohttp-3.13.5-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:57653eac22c6a4c13eb22ecf4d673d64a12f266e72785ab1c8b8e5940d0e8090", size = 1732476, upload-time = "2026-03-31T21:58:18.925Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/4e161f84f98d80c03a238671b4136e6530453d65262867d989bbe78244d0/aiohttp-3.13.5-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5e5f7debc7a57af53fdf5c5009f9391d9f4c12867049d509bf7bb164a6e295b", size = 1706507, upload-time = "2026-03-31T21:58:21.094Z" }, + { url = "https://files.pythonhosted.org/packages/d4/56/ea11a9f01518bd5a2a2fcee869d248c4b8a0cfa0bb13401574fa31adf4d4/aiohttp-3.13.5-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c719f65bebcdf6716f10e9eff80d27567f7892d8988c06de12bbbd39307c6e3a", size = 1773465, upload-time = "2026-03-31T21:58:23.159Z" }, + { url = "https://files.pythonhosted.org/packages/eb/40/333ca27fb74b0383f17c90570c748f7582501507307350a79d9f9f3c6eb1/aiohttp-3.13.5-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d97f93fdae594d886c5a866636397e2bcab146fd7a132fd6bb9ce182224452f8", size = 1873523, upload-time = "2026-03-31T21:58:25.59Z" }, + { url = "https://files.pythonhosted.org/packages/f0/d2/e2f77eef1acb7111405433c707dc735e63f67a56e176e72e9e7a2cd3f493/aiohttp-3.13.5-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3df334e39d4c2f899a914f1dba283c1aadc311790733f705182998c6f7cae665", size = 1754113, upload-time = "2026-03-31T21:58:27.624Z" }, + { url = "https://files.pythonhosted.org/packages/fb/56/3f653d7f53c89669301ec9e42c95233e2a0c0a6dd051269e6e678db4fdb0/aiohttp-3.13.5-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fe6970addfea9e5e081401bcbadf865d2b6da045472f58af08427e108d618540", size = 1562351, upload-time = "2026-03-31T21:58:29.918Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a6/9b3e91eb8ae791cce4ee736da02211c85c6f835f1bdfac0594a8a3b7018c/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7becdf835feff2f4f335d7477f121af787e3504b48b449ff737afb35869ba7bb", size = 1693205, upload-time = "2026-03-31T21:58:32.214Z" }, + { url = "https://files.pythonhosted.org/packages/98/fc/bfb437a99a2fcebd6b6eaec609571954de2ed424f01c352f4b5504371dd3/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:676e5651705ad5d8a70aeb8eb6936c436d8ebbd56e63436cb7dd9bb36d2a9a46", size = 1730618, upload-time = "2026-03-31T21:58:34.728Z" }, + { url = "https://files.pythonhosted.org/packages/e4/b6/c8534862126191a034f68153194c389addc285a0f1347d85096d349bbc15/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:9b16c653d38eb1a611cc898c41e76859ca27f119d25b53c12875fd0474ae31a8", size = 1745185, upload-time = "2026-03-31T21:58:36.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/93/4ca8ee2ef5236e2707e0fd5fecb10ce214aee1ff4ab307af9c558bda3b37/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:999802d5fa0389f58decd24b537c54aa63c01c3219ce17d1214cbda3c2b22d2d", size = 1557311, upload-time = "2026-03-31T21:58:39.38Z" }, + { url = "https://files.pythonhosted.org/packages/57/ae/76177b15f18c5f5d094f19901d284025db28eccc5ae374d1d254181d33f4/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:ec707059ee75732b1ba130ed5f9580fe10ff75180c812bc267ded039db5128c6", size = 1773147, upload-time = "2026-03-31T21:58:41.476Z" }, + { url = "https://files.pythonhosted.org/packages/01/a4/62f05a0a98d88af59d93b7fcac564e5f18f513cb7471696ac286db970d6a/aiohttp-3.13.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:2d6d44a5b48132053c2f6cd5c8cb14bc67e99a63594e336b0f2af81e94d5530c", size = 1730356, upload-time = "2026-03-31T21:58:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e4/85/fc8601f59dfa8c9523808281f2da571f8b4699685f9809a228adcc90838d/aiohttp-3.13.5-cp313-cp313-win32.whl", hash = "sha256:329f292ed14d38a6c4c435e465f48bebb47479fd676a0411936cc371643225cc", size = 432637, upload-time = "2026-03-31T21:58:46.167Z" }, + { url = "https://files.pythonhosted.org/packages/c0/1b/ac685a8882896acf0f6b31d689e3792199cfe7aba37969fa91da63a7fa27/aiohttp-3.13.5-cp313-cp313-win_amd64.whl", hash = "sha256:69f571de7500e0557801c0b51f4780482c0ec5fe2ac851af5a92cfce1af1cb83", size = 458896, upload-time = "2026-03-31T21:58:48.119Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ce/46572759afc859e867a5bc8ec3487315869013f59281ce61764f76d879de/aiohttp-3.13.5-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:eb4639f32fd4a9904ab8fb45bf3383ba71137f3d9d4ba25b3b3f3109977c5b8c", size = 745721, upload-time = "2026-03-31T21:58:50.229Z" }, + { url = "https://files.pythonhosted.org/packages/13/fe/8a2efd7626dbe6049b2ef8ace18ffda8a4dfcbe1bcff3ac30c0c7575c20b/aiohttp-3.13.5-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:7e5dc4311bd5ac493886c63cbf76ab579dbe4641268e7c74e48e774c74b6f2be", size = 497663, upload-time = "2026-03-31T21:58:52.232Z" }, + { url = "https://files.pythonhosted.org/packages/9b/91/cc8cc78a111826c54743d88651e1687008133c37e5ee615fee9b57990fac/aiohttp-3.13.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:756c3c304d394977519824449600adaf2be0ccee76d206ee339c5e76b70ded25", size = 499094, upload-time = "2026-03-31T21:58:54.566Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/a8362cb15cf16a3af7e86ed11962d5cd7d59b449202dc576cdc731310bde/aiohttp-3.13.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ecc26751323224cf8186efcf7fbcbc30f4e1d8c7970659daf25ad995e4032a56", size = 1726701, upload-time = "2026-03-31T21:58:56.864Z" }, + { url = "https://files.pythonhosted.org/packages/45/0c/c091ac5c3a17114bd76cbf85d674650969ddf93387876cf67f754204bd77/aiohttp-3.13.5-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:10a75acfcf794edf9d8db50e5a7ec5fc818b2a8d3f591ce93bc7b1210df016d2", size = 1683360, upload-time = "2026-03-31T21:58:59.072Z" }, + { url = "https://files.pythonhosted.org/packages/23/73/bcee1c2b79bc275e964d1446c55c54441a461938e70267c86afaae6fba27/aiohttp-3.13.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f7a18f258d124cd678c5fe072fe4432a4d5232b0657fca7c1847f599233c83a", size = 1773023, upload-time = "2026-03-31T21:59:01.776Z" }, + { url = "https://files.pythonhosted.org/packages/c7/ef/720e639df03004fee2d869f771799d8c23046dec47d5b81e396c7cda583a/aiohttp-3.13.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:df6104c009713d3a89621096f3e3e88cc323fd269dbd7c20afe18535094320be", size = 1853795, upload-time = "2026-03-31T21:59:04.568Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c9/989f4034fb46841208de7aeeac2c6d8300745ab4f28c42f629ba77c2d916/aiohttp-3.13.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:241a94f7de7c0c3b616627aaad530fe2cb620084a8b144d3be7b6ecfe95bae3b", size = 1730405, upload-time = "2026-03-31T21:59:07.221Z" }, + { url = "https://files.pythonhosted.org/packages/ce/75/ee1fd286ca7dc599d824b5651dad7b3be7ff8d9a7e7b3fe9820d9180f7db/aiohttp-3.13.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:c974fb66180e58709b6fc402846f13791240d180b74de81d23913abe48e96d94", size = 1558082, upload-time = "2026-03-31T21:59:09.484Z" }, + { url = "https://files.pythonhosted.org/packages/c3/20/1e9e6650dfc436340116b7aa89ff8cb2bbdf0abc11dfaceaad8f74273a10/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:6e27ea05d184afac78aabbac667450c75e54e35f62238d44463131bd3f96753d", size = 1692346, upload-time = "2026-03-31T21:59:12.068Z" }, + { url = "https://files.pythonhosted.org/packages/d8/40/8ebc6658d48ea630ac7903912fe0dd4e262f0e16825aa4c833c56c9f1f56/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a79a6d399cef33a11b6f004c67bb07741d91f2be01b8d712d52c75711b1e07c7", size = 1698891, upload-time = "2026-03-31T21:59:14.552Z" }, + { url = "https://files.pythonhosted.org/packages/d8/78/ea0ae5ec8ba7a5c10bdd6e318f1ba5e76fcde17db8275188772afc7917a4/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:c632ce9c0b534fbe25b52c974515ed674937c5b99f549a92127c85f771a78772", size = 1742113, upload-time = "2026-03-31T21:59:17.068Z" }, + { url = "https://files.pythonhosted.org/packages/8a/66/9d308ed71e3f2491be1acb8769d96c6f0c47d92099f3bc9119cada27b357/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:fceedde51fbd67ee2bcc8c0b33d0126cc8b51ef3bbde2f86662bd6d5a6f10ec5", size = 1553088, upload-time = "2026-03-31T21:59:19.541Z" }, + { url = "https://files.pythonhosted.org/packages/da/a6/6cc25ed8dfc6e00c90f5c6d126a98e2cf28957ad06fa1036bd34b6f24a2c/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f92995dfec9420bb69ae629abf422e516923ba79ba4403bc750d94fb4a6c68c1", size = 1757976, upload-time = "2026-03-31T21:59:22.311Z" }, + { url = "https://files.pythonhosted.org/packages/c1/2b/cce5b0ffe0de99c83e5e36d8f828e4161e415660a9f3e58339d07cce3006/aiohttp-3.13.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:20ae0ff08b1f2c8788d6fb85afcb798654ae6ba0b747575f8562de738078457b", size = 1712444, upload-time = "2026-03-31T21:59:24.635Z" }, + { url = "https://files.pythonhosted.org/packages/6c/cf/9e1795b4160c58d29421eafd1a69c6ce351e2f7c8d3c6b7e4ca44aea1a5b/aiohttp-3.13.5-cp314-cp314-win32.whl", hash = "sha256:b20df693de16f42b2472a9c485e1c948ee55524786a0a34345511afdd22246f3", size = 438128, upload-time = "2026-03-31T21:59:27.291Z" }, + { url = "https://files.pythonhosted.org/packages/22/4d/eaedff67fc805aeba4ba746aec891b4b24cebb1a7d078084b6300f79d063/aiohttp-3.13.5-cp314-cp314-win_amd64.whl", hash = "sha256:f85c6f327bf0b8c29da7d93b1cabb6363fb5e4e160a32fa241ed2dce21b73162", size = 464029, upload-time = "2026-03-31T21:59:29.429Z" }, + { url = "https://files.pythonhosted.org/packages/79/11/c27d9332ee20d68dd164dc12a6ecdef2e2e35ecc97ed6cf0d2442844624b/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:1efb06900858bb618ff5cee184ae2de5828896c448403d51fb633f09e109be0a", size = 778758, upload-time = "2026-03-31T21:59:31.547Z" }, + { url = "https://files.pythonhosted.org/packages/04/fb/377aead2e0a3ba5f09b7624f702a964bdf4f08b5b6728a9799830c80041e/aiohttp-3.13.5-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:fee86b7c4bd29bdaf0d53d14739b08a106fdda809ca5fe032a15f52fae5fe254", size = 512883, upload-time = "2026-03-31T21:59:34.098Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a6/aa109a33671f7a5d3bd78b46da9d852797c5e665bfda7d6b373f56bff2ec/aiohttp-3.13.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:20058e23909b9e65f9da62b396b77dfa95965cbe840f8def6e572538b1d32e36", size = 516668, upload-time = "2026-03-31T21:59:36.497Z" }, + { url = "https://files.pythonhosted.org/packages/79/b3/ca078f9f2fa9563c36fb8ef89053ea2bb146d6f792c5104574d49d8acb63/aiohttp-3.13.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8cf20a8d6868cb15a73cab329ffc07291ba8c22b1b88176026106ae39aa6df0f", size = 1883461, upload-time = "2026-03-31T21:59:38.723Z" }, + { url = "https://files.pythonhosted.org/packages/b7/e3/a7ad633ca1ca497b852233a3cce6906a56c3225fb6d9217b5e5e60b7419d/aiohttp-3.13.5-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:330f5da04c987f1d5bdb8ae189137c77139f36bd1cb23779ca1a354a4b027800", size = 1747661, upload-time = "2026-03-31T21:59:41.187Z" }, + { url = "https://files.pythonhosted.org/packages/33/b9/cd6fe579bed34a906d3d783fe60f2fa297ef55b27bb4538438ee49d4dc41/aiohttp-3.13.5-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6f1cbf0c7926d315c3c26c2da41fd2b5d2fe01ac0e157b78caefc51a782196cf", size = 1863800, upload-time = "2026-03-31T21:59:43.84Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3f/2c1e2f5144cefa889c8afd5cf431994c32f3b29da9961698ff4e3811b79a/aiohttp-3.13.5-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:53fc049ed6390d05423ba33103ded7281fe897cf97878f369a527070bd95795b", size = 1958382, upload-time = "2026-03-31T21:59:46.187Z" }, + { url = "https://files.pythonhosted.org/packages/66/1d/f31ec3f1013723b3babe3609e7f119c2c2fb6ef33da90061a705ef3e1bc8/aiohttp-3.13.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:898703aa2667e3c5ca4c54ca36cd73f58b7a38ef87a5606414799ebce4d3fd3a", size = 1803724, upload-time = "2026-03-31T21:59:48.656Z" }, + { url = "https://files.pythonhosted.org/packages/0e/b4/57712dfc6f1542f067daa81eb61da282fab3e6f1966fca25db06c4fc62d5/aiohttp-3.13.5-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:0494a01ca9584eea1e5fbd6d748e61ecff218c51b576ee1999c23db7066417d8", size = 1640027, upload-time = "2026-03-31T21:59:51.284Z" }, + { url = "https://files.pythonhosted.org/packages/25/3c/734c878fb43ec083d8e31bf029daae1beafeae582d1b35da234739e82ee7/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6cf81fe010b8c17b09495cbd15c1d35afbc8fb405c0c9cf4738e5ae3af1d65be", size = 1806644, upload-time = "2026-03-31T21:59:53.753Z" }, + { url = "https://files.pythonhosted.org/packages/20/a5/f671e5cbec1c21d044ff3078223f949748f3a7f86b14e34a365d74a5d21f/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:c564dd5f09ddc9d8f2c2d0a301cd30a79a2cc1b46dd1a73bef8f0038863d016b", size = 1791630, upload-time = "2026-03-31T21:59:56.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/fb8d0ad63a0b8a99be97deac8c04dacf0785721c158bdf23d679a87aa99e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:2994be9f6e51046c4f864598fd9abeb4fba6e88f0b2152422c9666dcd4aea9c6", size = 1809403, upload-time = "2026-03-31T21:59:59.103Z" }, + { url = "https://files.pythonhosted.org/packages/59/0c/bfed7f30662fcf12206481c2aac57dedee43fe1c49275e85b3a1e1742294/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:157826e2fa245d2ef46c83ea8a5faf77ca19355d278d425c29fda0beb3318037", size = 1634924, upload-time = "2026-03-31T22:00:02.116Z" }, + { url = "https://files.pythonhosted.org/packages/17/d6/fd518d668a09fd5a3319ae5e984d4d80b9a4b3df4e21c52f02251ef5a32e/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:a8aca50daa9493e9e13c0f566201a9006f080e7c50e5e90d0b06f53146a54500", size = 1836119, upload-time = "2026-03-31T22:00:04.756Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/15fb7a9d52e112a25b621c67b69c167805cb1f2ab8f1708a5c490d1b52fe/aiohttp-3.13.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:3b13560160d07e047a93f23aaa30718606493036253d5430887514715b67c9d9", size = 1772072, upload-time = "2026-03-31T22:00:07.494Z" }, + { url = "https://files.pythonhosted.org/packages/7e/df/57ba7f0c4a553fc2bd8b6321df236870ec6fd64a2a473a8a13d4f733214e/aiohttp-3.13.5-cp314-cp314t-win32.whl", hash = "sha256:9a0f4474b6ea6818b41f82172d799e4b3d29e22c2c520ce4357856fced9af2f8", size = 471819, upload-time = "2026-03-31T22:00:10.277Z" }, + { url = "https://files.pythonhosted.org/packages/62/29/2f8418269e46454a26171bfdd6a055d74febf32234e474930f2f60a17145/aiohttp-3.13.5-cp314-cp314t-win_amd64.whl", hash = "sha256:18a2f6c1182c51baa1d28d68fea51513cb2a76612f038853c0ad3c145423d3d9", size = 505441, upload-time = "2026-03-31T22:00:12.791Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a5/630bc484695d4a1342bbae85fb8689bf979106525684fc88f05b397324ad/aiohttp-3.13.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:347542f0ea3f95b2a955ee6656461fa1c776e401ac50ebce055a6c38454a0adf", size = 752872, upload-time = "2026-03-31T22:00:15.553Z" }, + { url = "https://files.pythonhosted.org/packages/cd/b8/6a19dda37fda94a9ebefb3c1ae0ff419ac7fbf4fb40750e992829fc13614/aiohttp-3.13.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:178c7b5e62b454c2bc790786e6058c3cc968613b4419251b478c153a4aec32b1", size = 504582, upload-time = "2026-03-31T22:00:18.191Z" }, + { url = "https://files.pythonhosted.org/packages/d5/34/8413eafee3421ade2d6ce9e7c0da1213e1d7f0049be09dcdc342b03a39ba/aiohttp-3.13.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:af545c2cffdb0967a96b6249e6f5f7b0d92cdfd267f9d5238d5b9ca63e8edb10", size = 499094, upload-time = "2026-03-31T22:00:21.118Z" }, + { url = "https://files.pythonhosted.org/packages/da/cf/c6f97006093d1e8ca40fbab843ff49ec7725ab668f0714dd1cb702c62cbd/aiohttp-3.13.5-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:206b7b3ef96e4ce211754f0cd003feb28b7d81f0ad26b8d077a5d5161436067f", size = 1669505, upload-time = "2026-03-31T22:00:24.01Z" }, + { url = "https://files.pythonhosted.org/packages/c2/27/3b2288e66dcec8b04771b2bee3909f70e4072bea995cde5ab7e775e73ddc/aiohttp-3.13.5-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:ee5e86776273de1795947d17bddd6bb19e0365fd2af4289c0d2c5454b6b1d36b", size = 1648928, upload-time = "2026-03-31T22:00:27.001Z" }, + { url = "https://files.pythonhosted.org/packages/3a/7f/605d766887594a88dcc27a19663499c7c5e13e7aa87f129b763765a2ee63/aiohttp-3.13.5-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:95d14ca7abefde230f7639ec136ade282655431fd5db03c343b19dda72dd1643", size = 1731800, upload-time = "2026-03-31T22:00:29.603Z" }, + { url = "https://files.pythonhosted.org/packages/71/94/5a878e728e30699d22b118f1a6ad576ab6fff9eb2c6fc8a7faa9376a1c3e/aiohttp-3.13.5-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:912d4b6af530ddb1338a66229dac3a25ff11d4448be3ec3d6340583995f56031", size = 1824247, upload-time = "2026-03-31T22:00:32.139Z" }, + { url = "https://files.pythonhosted.org/packages/37/99/84b448291e9996bb83bf4fad3a71a9786d542f19c50a3ff0531bfaba6fac/aiohttp-3.13.5-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e999f0c88a458c836d5fb521814e92ed2172c649200336a6df514987c1488258", size = 1670742, upload-time = "2026-03-31T22:00:34.788Z" }, + { url = "https://files.pythonhosted.org/packages/14/a8/d8d5d1ab6d29a4a3bdb9db31f161e338bfdf6638f6574ea8380f1d4a243c/aiohttp-3.13.5-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:39380e12bd1f2fdab4285b6e055ad48efbaed5c836433b142ed4f5b9be71036a", size = 1562474, upload-time = "2026-03-31T22:00:37.623Z" }, + { url = "https://files.pythonhosted.org/packages/92/e8/bd889697916f10b65524422c61b4eeaf919eb35a170290cccb680cbe4eb4/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9efcc0f11d850cefcafdd9275b9576ad3bfb539bed96807663b32ad99c4d4b88", size = 1642235, upload-time = "2026-03-31T22:00:40.541Z" }, + { url = "https://files.pythonhosted.org/packages/60/42/3f1928107131f1413a5972ace14ddcd5364968e9bd7b3ad71272defafc9c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:147b4f501d0292077f29d5268c16bb7c864a1f054d7001c4c1812c0421ea1ed0", size = 1655397, upload-time = "2026-03-31T22:00:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/b2/79/c4bbcf4cac3a4715a326e49720ccdc3a4b5e14a367c5029eae7727d06029/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:d147004fede1b12f6013a6dbb2a26a986a671a03c6ea740ddc76500e5f1c399f", size = 1703509, upload-time = "2026-03-31T22:00:45.908Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e6/32d245876f211a7308a7d5437707f9296b1f9837a2888a407ed04e61321c/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:9277145d36a01653863899c665243871434694bcc3431922c3b35c978061bdb8", size = 1550098, upload-time = "2026-03-31T22:00:49.48Z" }, + { url = "https://files.pythonhosted.org/packages/db/62/ab0f1304def56ce2356e6fbb9f0b024d6544010351430070f48f53b89e0a/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4e704c52438f66fdd89588346183d898bb42167cf88f8b7ff1c0f9fc957c348f", size = 1724326, upload-time = "2026-03-31T22:00:52.165Z" }, + { url = "https://files.pythonhosted.org/packages/c4/9a/aab4469689024046220ea438aa020ea2ae04cd1dd71aea3057e094f8c357/aiohttp-3.13.5-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:a8a4d3427e8de1312ddf309cc482186466c79895b3a139fed3259fc01dfa9a5b", size = 1658824, upload-time = "2026-03-31T22:00:55.122Z" }, + { url = "https://files.pythonhosted.org/packages/b0/98/bcc35d4db687acabf06d41f561a99fa88bca145292513388c858d99b72c5/aiohttp-3.13.5-cp39-cp39-win32.whl", hash = "sha256:6f497a6876aa4b1a102b04996ce4c1170c7040d83faa9387dd921c16e30d5c83", size = 440302, upload-time = "2026-03-31T22:00:57.673Z" }, + { url = "https://files.pythonhosted.org/packages/25/61/b0203c2ef6bd268fca0eda142f0efbba7cbebd7ad38f7bb01dd31c2ff68e/aiohttp-3.13.5-cp39-cp39-win_amd64.whl", hash = "sha256:cb979826071c0986a5f08333a36104153478ce6018c58cba7f9caddaf63d5d67", size = 463076, upload-time = "2026-03-31T22:01:00.264Z" }, +] + +[[package]] +name = "aiohttp" +version = "3.14.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "aiohappyeyeballs", marker = "python_full_version >= '3.10'" }, + { name = "aiosignal", marker = "python_full_version >= '3.10'" }, + { name = "async-timeout", marker = "python_full_version == '3.10.*'" }, + { name = "attrs", marker = "python_full_version >= '3.10'" }, + { name = "frozenlist", marker = "python_full_version >= '3.10'" }, + { name = "multidict", marker = "python_full_version >= '3.10'" }, + { name = "propcache", marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, + { name = "yarl", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/82/78/8ea7308cac6934de8c74a14f3d5f65d1c89287426688be79538d0e5c013d/aiohttp-3.14.1.tar.gz", hash = "sha256:307f2cff90a764d329e77040603fa032db89c5c24fdad50c4c15334cba744035", size = 7955794, upload-time = "2026-06-07T21:09:35.529Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6d/67/58ded4b3f2e10f94972d8928050c85330e249a31dd45a0e5f3c0e9c3fa05/aiohttp-3.14.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8f6bb621e5863cfe8fe5ff5468002d200ec31f30f1280b259dc505b02595099e", size = 766140, upload-time = "2026-06-07T21:05:37.471Z" }, + { url = "https://files.pythonhosted.org/packages/18/68/4ae5b4e08943f316594bb68da89957d3baf5760588fa09509594bd777e4b/aiohttp-3.14.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:4f7215cb3933784f79ed20e5f050e15984f390424339b22375d5a53c933a0491", size = 519430, upload-time = "2026-06-07T21:05:40.751Z" }, + { url = "https://files.pythonhosted.org/packages/cb/c1/316c8f3549dbe5245f92bfd523ec6f32dd4d98cafe21df3f6a19b1184c75/aiohttp-3.14.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d9d4e294455b23a68c9b8f042d0e8e377a265bcb15332753695f6e5b6819e0ce", size = 514406, upload-time = "2026-06-07T21:05:42.111Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ee/fb0ac28684e8d753b83c8a4eebc19a5846912aa0a4daaabb6a9936363840/aiohttp-3.14.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b238af795833d5731d049d82bc84b768ae6f8f97f0495963b3ed9935c5901cc3", size = 1703649, upload-time = "2026-06-07T21:05:43.427Z" }, + { url = "https://files.pythonhosted.org/packages/3b/57/aa2beab673331f111885db8a7b69dfe3ab0e53e446a0ace18ca694b4dc58/aiohttp-3.14.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e4e5e0ae56914ecdbf446493addefc0159053dd53962cef37d7839f37f73d505", size = 1675126, upload-time = "2026-06-07T21:05:44.897Z" }, + { url = "https://files.pythonhosted.org/packages/47/ea/dad128abe365e79be03b16ed464198ac73e0d257e8260c6f7d6f31cbef26/aiohttp-3.14.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:092e4ce3619a7c6dee52a6bdabda973d9b34b66781f840ce93c7e0cec30cf521", size = 1771558, upload-time = "2026-06-07T21:05:46.405Z" }, + { url = "https://files.pythonhosted.org/packages/63/f3/b5b4e10327cb85d34d24232c6b71b64602f190b3ccb238a043ac6b187dac/aiohttp-3.14.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb33777ea21e8b7ecde0e6fc84f598be0a1192eab1a63bc746d75aa75d38e7bd", size = 1856631, upload-time = "2026-06-07T21:05:47.844Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9d/93294c3045775c708ac8310eb3d3622a11d2951345ad590d532d62a1faa4/aiohttp-3.14.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:23119f8fd4f5d16902ed459b63b100bcd269628075162bddac56cc7b5273b3fb", size = 1714139, upload-time = "2026-06-07T21:05:49.982Z" }, + { url = "https://files.pythonhosted.org/packages/29/c4/93067c85a0373492ce8e577435203c5947c454af074ac48ed4f3a1b9dd4a/aiohttp-3.14.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:57fc6745a4b7d0f5a9eb4f40a69718be6c0bc1b8368cc9fe89e90118719f4f42", size = 1588321, upload-time = "2026-06-07T21:05:51.431Z" }, + { url = "https://files.pythonhosted.org/packages/c4/39/9ff91aaf02af8b7b8222a987466da539f154c3e01732c22b5f5a20a8ee66/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:6fd35beba67c4183b09375c5fff9accb47524191a244a99f95fd4472f5402c2b", size = 1670375, upload-time = "2026-06-07T21:05:53.109Z" }, + { url = "https://files.pythonhosted.org/packages/aa/e4/77452a3676b8d99ac1375f77691d6bf65ea6e9f4b201b82ef77c916dc767/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:672b9d65f42eb877f5c3f234a4547e4e1a226ca8c2eed879bb34670a0ce51192", size = 1690933, upload-time = "2026-06-07T21:05:54.902Z" }, + { url = "https://files.pythonhosted.org/packages/7d/84/b0059a7c7fc05ea23f3bc1596ba91c12f79588b9450564a24cac37536d0a/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:24ba13339fed9251d9b1a1bec8c7ab84c0d1675d79d33501e11f94f8b9a84e05", size = 1740798, upload-time = "2026-06-07T21:05:56.458Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3a/e2a513ecbfc362591caa51a7f7e011b3bfc8938b388ae44cd95560d36999/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:94da27378da0610e341c4d30de29a191672683cc82b8f9556e8f7c7212a020fe", size = 1576412, upload-time = "2026-06-07T21:05:57.953Z" }, + { url = "https://files.pythonhosted.org/packages/a1/10/08f1654f538f93d36dcac66310a06eefce4641cdafca83f9f0a5317be254/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:52cdac9432d8b4a719f35094a818d95adcae0f0b4fe9b9b921909e0c87de9e7d", size = 1750199, upload-time = "2026-06-07T21:05:59.488Z" }, + { url = "https://files.pythonhosted.org/packages/99/e4/d91b70c57d8b8e9611e4a2e52238ca3698d3dc1c2efe25b7a9bf594ac584/aiohttp-3.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:672ac254412a24d0d0cf00a9e6c238877e4be5e5fa2d188832c1244f45f31966", size = 1699356, upload-time = "2026-06-07T21:06:01.131Z" }, + { url = "https://files.pythonhosted.org/packages/3d/f1/15340176f35ff61b95dbe34020bcf43f9e624a2d7bbac934715ff97d2033/aiohttp-3.14.1-cp310-cp310-win32.whl", hash = "sha256:2fe3607e71acc6ebb0ec8e492a247bf7a291226192dc0084236dfc12478916f6", size = 458939, upload-time = "2026-06-07T21:06:02.86Z" }, + { url = "https://files.pythonhosted.org/packages/c3/c2/a2f1ec5b37f903109e43ae2862268cfe4a67a60c1b2cf43169fcdff5995f/aiohttp-3.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:30099eda75a53c32efb0920e9c33c195314d2cc1c680fbfd30894932ac5f27df", size = 482583, upload-time = "2026-06-07T21:06:04.666Z" }, + { url = "https://files.pythonhosted.org/packages/d0/7a/7b56f6732ef79530afaa72aa335d41b67c8d79b946995f0b11ad72985435/aiohttp-3.14.1-cp310-cp310-win_arm64.whl", hash = "sha256:5a837f49d901f9e368651b676912bff1104ed8c1a83b280bcd7b29adccef5c9c", size = 453470, upload-time = "2026-06-07T21:06:06.322Z" }, + { url = "https://files.pythonhosted.org/packages/26/dd/bf526e6f0a1120dd6f2df2e97bacfe4d358f13d17a0ff5847301a1375a51/aiohttp-3.14.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:aa00140699487bd435fde4342d85c94cb256b7cd3a5b9c3396c67f19922afda2", size = 765225, upload-time = "2026-06-07T21:06:07.957Z" }, + { url = "https://files.pythonhosted.org/packages/8f/e1/a2872aa55495a70f61310d411541c6ee23812d9a884e000c716e1bc3edbf/aiohttp-3.14.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1c1af67559445498b502030c35c59db59966f47041ca9de5b4e707f86bd10b5f", size = 518743, upload-time = "2026-06-07T21:06:09.749Z" }, + { url = "https://files.pythonhosted.org/packages/5b/e7/c60c7b209e509cc787de3cea0550a518538cfc08003e1c1e14c1c63fff71/aiohttp-3.14.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d44ec478e713ee7f29b439f7eb8dc2b9d4079e11ae114d2c2ac3d5daf30516c8", size = 514139, upload-time = "2026-06-07T21:06:11.26Z" }, + { url = "https://files.pythonhosted.org/packages/5b/8d/614ace2f579702c9840ab1e1447fd8509e35b0b904f7196418fa2f57b25d/aiohttp-3.14.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d3b1a184a9a8f548a6b73f1e26b96b052193e4b3175ed7342aaf1151a1f00a04", size = 1784088, upload-time = "2026-06-07T21:06:12.887Z" }, + { url = "https://files.pythonhosted.org/packages/49/e0/726e90f99542bf292f81a96a12cc4847deb86f3ccf62c6f4014a201f4d33/aiohttp-3.14.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5f2504bc0322437c9a1ff6d3333ca56c7477b727c995f036b976ae17b98372c8", size = 1737835, upload-time = "2026-06-07T21:06:14.564Z" }, + { url = "https://files.pythonhosted.org/packages/0b/4b/d176d5c4db9d33dacf0543102ea59503bc1d528af4cfd0b719949ca49389/aiohttp-3.14.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:73f05ea02013e02512c3bf42714f1208c57168c779cc6fe23516e4543089d0a6", size = 1842801, upload-time = "2026-06-07T21:06:16.228Z" }, + { url = "https://files.pythonhosted.org/packages/dc/d6/5a99b563690ea0cbed912ae94a2ce33993a5709a651a3a4fe761e7dd973a/aiohttp-3.14.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:797457503c2d426bee06eef808d07b31ede30b65e054444e7de64cad0061b7af", size = 1929992, upload-time = "2026-06-07T21:06:17.947Z" }, + { url = "https://files.pythonhosted.org/packages/76/7f/a987b14a3859094b3cea3f4825219c3e5536242564af6e3f9c2f6c994eb2/aiohttp-3.14.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b821a1f7dedf7e37450654e620038ac3b2e81e8fa6ea269337e97101978ec730", size = 1786989, upload-time = "2026-06-07T21:06:19.677Z" }, + { url = "https://files.pythonhosted.org/packages/f1/1a/420e5c85a3e73349372ed22ce0b6af86bfa6ce16a4b20a64a2e94608c781/aiohttp-3.14.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4cd96b5ba05d67ed0cf00b5b405c8cd99586d8e3481e8ee0a831057591af7621", size = 1640129, upload-time = "2026-06-07T21:06:22.558Z" }, + { url = "https://files.pythonhosted.org/packages/a7/80/18a592ed3be0a402cc03670bd72ee1f8563ddbe1d8d5542dbf868f274136/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1d459b98a932296c6f0e94f87511a0b1b90a8a02c30a50e60a297619cd5a58ee", size = 1756576, upload-time = "2026-06-07T21:06:24.8Z" }, + { url = "https://files.pythonhosted.org/packages/ec/0b/8b3d5713373858ff71a617daf6e3b0e81ad63e79d09a3cf2f6b6b983939c/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:764457a7be60825fb770a644852ff717bcbb5042f189f2bd16df61a81b3f6573", size = 1754668, upload-time = "2026-06-07T21:06:26.528Z" }, + { url = "https://files.pythonhosted.org/packages/9f/49/fd564575cf225821d7ba5a117cb8bc27213d8a7e1811162afb43ae077039/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f7a16ef45b081454ef844502d87a848876c490c4cb5c650c230f6ec79ed2c1e7", size = 1817019, upload-time = "2026-06-07T21:06:28.297Z" }, + { url = "https://files.pythonhosted.org/packages/ed/1b/e850c9ae6fc91356552ae668bb6c51e93fa29c8aef13398a10b56678557f/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:2fbc3ed048b3475b9f0cbcb9978e9d2d3511acd91ead203af26ed9f0056004cf", size = 1631638, upload-time = "2026-06-07T21:06:30.242Z" }, + { url = "https://files.pythonhosted.org/packages/eb/94/3c337ba72451a89806ace6f75bddc92bafc5b8d53d90115a512858024b63/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:bedb0cd073cc2dc035e30aeb99444389d3cd2113afe4ef9fcd23d439f5bade85", size = 1835660, upload-time = "2026-06-07T21:06:31.943Z" }, + { url = "https://files.pythonhosted.org/packages/2b/9c/9c18cf367a0498212d9ba7daf990b504a5e8ae064cda4b504e2647c89c03/aiohttp-3.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b6feea921016eb3d4e04d65fc4e9ca402d1a3801f562aef94989f54694917af3", size = 1775698, upload-time = "2026-06-07T21:06:33.72Z" }, + { url = "https://files.pythonhosted.org/packages/b5/63/a251a9d2a6cb45065b2ddc0bde2b3dd10108740a9a42f632c66405a761a2/aiohttp-3.14.1-cp311-cp311-win32.whl", hash = "sha256:313701e488100074ce99850404ee36e741abf6330179fec908a1944ecf570126", size = 458386, upload-time = "2026-06-07T21:06:35.279Z" }, + { url = "https://files.pythonhosted.org/packages/17/ca/69274c51dcd6e8947d77b2806cf47a4a15f2c846e2cbeb1882547d3da283/aiohttp-3.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:03ab4530fdcb3a543a122ba4b65ac9919da9fe9f78a03d328a6e38ff962f7aa5", size = 483406, upload-time = "2026-06-07T21:06:36.824Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8a/c25904f77690c3688ec140f87591ef11a0cfe36bf3d5c0f1f38056fb62b3/aiohttp-3.14.1-cp311-cp311-win_arm64.whl", hash = "sha256:486f7d16ed54c39c2cbd7ca71fd8ba2b8bb7860df65bd7b6ed640bab96a38a8b", size = 452987, upload-time = "2026-06-07T21:06:38.371Z" }, + { url = "https://files.pythonhosted.org/packages/1d/21/151624b51cd92553d95424daf4bf19f19ce9be9002d19253e7e7ce67197b/aiohttp-3.14.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:d35143e27778b4bb0fb189562d7f275bff79c62ab8e98459717c0ea617ff2480", size = 757402, upload-time = "2026-06-07T21:06:40.311Z" }, + { url = "https://files.pythonhosted.org/packages/c2/82/280619e0bd7bf2454987e19282616e84762255dd9c8468f62382e8c191f1/aiohttp-3.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bcfb80a2cc36fba2534e5e5b5264dc7ae6fcd9bf15256da3e53d2f499e6fa29d", size = 512310, upload-time = "2026-06-07T21:06:42.207Z" }, + { url = "https://files.pythonhosted.org/packages/55/b2/2aac325583aaa1353045f96dffa586d8a34e8322e14a7ba49cffeb103ab4/aiohttp-3.14.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:27fd7c91e51729b4f7e1577865fa6d34c9adccbc39aabe9000285b48af9f0ec2", size = 512448, upload-time = "2026-06-07T21:06:43.813Z" }, + { url = "https://files.pythonhosted.org/packages/8a/72/a60607cb849faa8af8a356c9329ea2eb6f395d49e82cc82ccba1fd8deb8f/aiohttp-3.14.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:64c567bf9eaf664280116a8688f63016e6b32db2505908e2bdaca1b6438142f2", size = 1766854, upload-time = "2026-06-07T21:06:45.391Z" }, + { url = "https://files.pythonhosted.org/packages/b5/d3/d9fe1c9ec7557ab4d0d82bebaa728c6418f0b93295ec2f4ab015f7710cc7/aiohttp-3.14.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:f5e6ff2bdbb8f4cd3fbe41f99e25bbcd58e3bf9f13d3dd31a11e7917251cc77a", size = 1740884, upload-time = "2026-06-07T21:06:47.413Z" }, + { url = "https://files.pythonhosted.org/packages/c1/dc/f2cecfaf9337ba3e63f181500814ff502aa3d00d9c7ec93a9d23d10a27b2/aiohttp-3.14.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2f73e01dc37122325caf079982621262f96d74823c179038a82fddfc50359264", size = 1810034, upload-time = "2026-06-07T21:06:50.165Z" }, + { url = "https://files.pythonhosted.org/packages/66/d7/2ff65c5e65c0d7476daf7e15c032e0805e36811185b9623e3238ad6c763e/aiohttp-3.14.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb2c0c80d431c0d03f2c7dbf125150fedd4f0de17366a7ca33f7ccb822391842", size = 1904054, upload-time = "2026-06-07T21:06:52.035Z" }, + { url = "https://files.pythonhosted.org/packages/20/9c/d445818389df371f56d141d881153ba23183c4735a03f7356ffb43f7757d/aiohttp-3.14.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3e6fc1a85fa7194a1a7d19f44e8609180f4a8eb5fa4c7ed8b4355f080fad235c", size = 1790278, upload-time = "2026-06-07T21:06:54.049Z" }, + { url = "https://files.pythonhosted.org/packages/4d/aa/bf04cb4d865fc6101c2229a294ad744973b72e513fdc5a6b791e6983d72a/aiohttp-3.14.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:686b6c0d3911ec387b444ddf5dc62fb7f7c0a7d5186a7861626496a5ab4aff95", size = 1591795, upload-time = "2026-06-07T21:06:55.911Z" }, + { url = "https://files.pythonhosted.org/packages/dc/b4/4dac0038960427ba832f6609dfb4ea5437d7fd80c72001b9e48f834f428b/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c6fa4dc7ad6f8109c70bb1499e589f76b0b792baf39f9b017eb92c8a81d0a199", size = 1728397, upload-time = "2026-06-07T21:06:57.777Z" }, + { url = "https://files.pythonhosted.org/packages/2b/f9/7cd4e8ad7aa3b75f17d56bb5498dd604a93d4e6eece822ba0568c413fff0/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:87a5eea1b2a5e21e1ebdbb33ad4165359189327e63fc4e4894693e7f821ac817", size = 1766504, upload-time = "2026-06-07T21:07:00.009Z" }, + { url = "https://files.pythonhosted.org/packages/f9/df/fc01d9fcad0f73fed3f3d361f1f94f975947b50dff82919f6dc2bf4316cc/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:1c1421eb01d4fd608d88cc8290211d177a58532b55ad94076fb349c5bf467f0a", size = 1777806, upload-time = "2026-06-07T21:07:02.064Z" }, + { url = "https://files.pythonhosted.org/packages/41/09/47e2d090bddcc8fb4ccb4c314aadc32d7c5d9bb55f50f6ad1c92fc15d501/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:34b257ec41345c1e8f2df68fa908a7952f5de932723871eb633ecbbff396c9a4", size = 1580707, upload-time = "2026-06-07T21:07:03.942Z" }, + { url = "https://files.pythonhosted.org/packages/3d/36/f1a4ce904ae0b6930cfe9afc96d0896f7ec1a620c400405d63783bb95a9c/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:de538791a80e5d862addbc183f70f0158ac9b9bb872bb147f1fd2a683691e087", size = 1798121, upload-time = "2026-06-07T21:07:05.987Z" }, + { url = "https://files.pythonhosted.org/packages/70/0a/e0075ce9ca0279ee1d4f0c0b85f54fea02ebc83c3007651a72bece658fec/aiohttp-3.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f71173be42d3241d428f760122febb748de0623f44308a6f120d0dd9ec572e3", size = 1767580, upload-time = "2026-06-07T21:07:07.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/a0c0a8f327a9c52095cdd8e312391b00d3ed64ab6c72bb5c33d8ec251cf7/aiohttp-3.14.1-cp312-cp312-win32.whl", hash = "sha256:ec8dc383ee57ea3e883477dcca3f11b65d58199f1080acaf4cd6ad9a99698be4", size = 452771, upload-time = "2026-06-07T21:07:09.669Z" }, + { url = "https://files.pythonhosted.org/packages/df/d9/ea367c75f16ac9c6cdc8febb25e8318fa21a2b1bc8d6514d4b2d890bface/aiohttp-3.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2aa92c87868cd13674989f9ee83e5f9f7ea4237589b728048e1f0c8f6caa3271", size = 479873, upload-time = "2026-06-07T21:07:11.538Z" }, + { url = "https://files.pythonhosted.org/packages/03/64/8d96784a7851156db8a4c6c3f6f91042fdf39fb15a4cc38c8b3c14833c45/aiohttp-3.14.1-cp312-cp312-win_arm64.whl", hash = "sha256:2c840c90759922cb5e6dda94596e079a30fb5a5ba548e7e0dc00574703940847", size = 448073, upload-time = "2026-06-07T21:07:13.637Z" }, + { url = "https://files.pythonhosted.org/packages/bc/97/bd137012dd97e1649162b099135a80e1fd59aaa807b2430fc448d1029aff/aiohttp-3.14.1-cp313-cp313-android_21_arm64_v8a.whl", hash = "sha256:b3a03285a7f9c7b016324574a6d92a1c895da6b978cb8f1deee3ac72bc6da178", size = 506882, upload-time = "2026-06-07T21:07:15.501Z" }, + { url = "https://files.pythonhosted.org/packages/ef/79/e5cc690e9d922a66887ceeaca53a8ffd5a7b0be3816142b7abc433742d89/aiohttp-3.14.1-cp313-cp313-android_21_x86_64.whl", hash = "sha256:2a73f487ab8ef5abbb24b7aa9b73e98eaba9e9e031804ff2416f02eca315ccaf", size = 515270, upload-time = "2026-06-07T21:07:17.53Z" }, + { url = "https://files.pythonhosted.org/packages/fe/22/a73ccbf9dbd6e26dda0b24d5fd5db7da92ee3383a79f47677ffb834c5c5b/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:915fbb7b41b115192259f8c9ae58f3ddc444d2b5579917270211858e606a4afd", size = 485841, upload-time = "2026-06-07T21:07:19.555Z" }, + { url = "https://files.pythonhosted.org/packages/3b/b9/57ed8eaf596321c2ad747bd480fb1700dbd7177c60dfc9e4c187f629662e/aiohttp-3.14.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:7fb4bdf95b0561a79f259f9d28fbc109728c5ee7f27aff6391f0ca703a329abe", size = 492088, upload-time = "2026-06-07T21:07:21.581Z" }, + { url = "https://files.pythonhosted.org/packages/78/c0/5ebe5270a7c140d7c6f79dcb018640225f14d406c149e4eec04a7d82fe71/aiohttp-3.14.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1b9748363260121d2927704f5d4fc498150669ca3ae93625986ee89c8f80dcd4", size = 501564, upload-time = "2026-06-07T21:07:23.388Z" }, + { url = "https://files.pythonhosted.org/packages/75/7f/8cdaa24fc7983865e0915153b96a9ac5bcdd3548d64c5a27d17cecccad2d/aiohttp-3.14.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:86a6dab78b0e43e2897a3bbe15745aa60dc5423ca437b7b0b164c069bf91b876", size = 751998, upload-time = "2026-06-07T21:07:25.046Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f4/c4227aacfacc5cb0cc2d119b65301d177912a6842cd64e120c47af76064f/aiohttp-3.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:4dfd6e47d3c44c2279907607f73a4240b88c69eb8b90da7e2441a8045dfd21da", size = 510918, upload-time = "2026-06-07T21:07:27.28Z" }, + { url = "https://files.pythonhosted.org/packages/ab/01/a2d5f96cd4e74424864d30bc0a7e44d0a12dacdcfa91b5b2d1bd3dca6bf3/aiohttp-3.14.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:317acd9f8602858dc7d59679812c376c7f0b97bcbbf16e0d6237f54141d8a8a6", size = 508657, upload-time = "2026-06-07T21:07:29.252Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ed/3c0fb5c500fdd8e7ebc10d1889c04384fffa1a9163eac1356088ca9da1b1/aiohttp-3.14.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd869c427324e5cb15195793de951295710db28be7d818247f3097b4ab5d4b96", size = 1757907, upload-time = "2026-06-07T21:07:31.03Z" }, + { url = "https://files.pythonhosted.org/packages/0b/ab/d4c924d9bd5be3050c226612413ce68cb54c70d2c31b661bfc8d9a5b6a70/aiohttp-3.14.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:93b032b5ec3255473c143627d21a69ac74ae12f7f33974cb587c564d11b1066f", size = 1737565, upload-time = "2026-06-07T21:07:33.031Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/37326821ff779084020cdc33224d20b19f42f4183a500ff92022a739eda7/aiohttp-3.14.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f234b4deb12f3ad59127e037bc57c40c21e45b45282df7d3a55a0f409f595296", size = 1799018, upload-time = "2026-06-07T21:07:35.003Z" }, + { url = "https://files.pythonhosted.org/packages/b3/4f/6e947ba73e4ce09070761c05ed3a8ceb7c21f5e46798671d8b2aac0e4626/aiohttp-3.14.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9af6779bfb46abf124068327abcdf9ce95c9ef8287a3e8da76ccf2d0f16c28fa", size = 1894416, upload-time = "2026-06-07T21:07:36.956Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6e/dbf1d0625dc711fb2851f4f3c3055c39ed58bae92082d8c627dbe6013736/aiohttp-3.14.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:faccab372e66bc76d5731525e7f1143c922271725b9d38c9f97edcc66266b451", size = 1783881, upload-time = "2026-06-07T21:07:39.063Z" }, + { url = "https://files.pythonhosted.org/packages/44/c2/5e25098a67268ed369483ae7d1a58bd0a13d03aab860d2a0e4a6eb25b046/aiohttp-3.14.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:f380468b09d2a81633ee863b0ec5648d364bd17bb8ecfb8c2f387f7ac1faf42c", size = 1587572, upload-time = "2026-06-07T21:07:41.058Z" }, + { url = "https://files.pythonhosted.org/packages/2a/bd/cf9cee17e140f942a3de73e658a543aa8fbf35a5fc67a9d2538d52d77f0b/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:97e704dcd26271f5bda3fa07c3ce0fb76d6d3f8659f4baa1a24442cc9ba177ca", size = 1722137, upload-time = "2026-06-07T21:07:43.014Z" }, + { url = "https://files.pythonhosted.org/packages/89/6d/5684f8c59045c96f81a18cefbc1fbbd79d25b88f1c622f2a5c5c08fcb632/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:269b76ac5394092b95bc4a098f4fc6c191c083c3bd12775d1e30e663132f6a09", size = 1755953, upload-time = "2026-06-07T21:07:45.933Z" }, + { url = "https://files.pythonhosted.org/packages/a8/40/35caf3170f8359760740a7d9aa0fff2e344bef98e1d1186f5a0f6dec17e6/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c0b3e614340c889d575451696374c9d17affd54cd607ca0babed8f8c37b9397", size = 1766479, upload-time = "2026-06-07T21:07:48.047Z" }, + { url = "https://files.pythonhosted.org/packages/6d/a1/b0c61e7a137f0d81de49a82023a6df73c3c16d6fefb0f8e4a93d21639002/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:5663ee9257cfa1add7253a7da3035a02f31b6600ec48261585e1800a81533080", size = 1580077, upload-time = "2026-06-07T21:07:50.069Z" }, + { url = "https://files.pythonhosted.org/packages/0b/41/194ea4623693009fcefebef7aef63c141754f153e9cd0d39d3b9e36c175c/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:603a2c834142172ffddc054067f5ec0ca65d57a0aa98a71bc81952573208e345", size = 1791688, upload-time = "2026-06-07T21:07:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/ba/45/4de841f005cfe1fd63e2a2fe011262c515e2a62aa6994b15947e7d717ac9/aiohttp-3.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cb21957bb8aca671c1765e32f58164cf0c50e6bf41c0bbbd16da20732ecaf588", size = 1761094, upload-time = "2026-06-07T21:07:54.113Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ae/dbce10533d3896d544d5053939ed75b7dc31a1b0973d959b1b5ae21028d6/aiohttp-3.14.1-cp313-cp313-win32.whl", hash = "sha256:e509a55f681e6158c20f70f102f9cf61fb20fbc382272bc6d94b7343f2582780", size = 452662, upload-time = "2026-06-07T21:07:56.06Z" }, + { url = "https://files.pythonhosted.org/packages/7b/d9/0bf1a19362c32f06229da5e7ddfcec91f93474d6307f7a2d3135e9c674dc/aiohttp-3.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:1ac8531b638959718e18c2207fbfe297819875da46a740b29dfa29beba64355a", size = 479748, upload-time = "2026-06-07T21:07:58.319Z" }, + { url = "https://files.pythonhosted.org/packages/22/0a/62e7232dc9484fbec112ceb32efb6a624cc7994ec6e2b019286f17c4e8f2/aiohttp-3.14.1-cp313-cp313-win_arm64.whl", hash = "sha256:250d14af67f6b6a1a4a811049b1afa69d61d617fca6bf33149b3ab1a6dbcf7b8", size = 447723, upload-time = "2026-06-07T21:08:00.154Z" }, + { url = "https://files.pythonhosted.org/packages/c4/a1/5fafa04e1ca91ddb47608699d60649c1c6db3cf41c99e78fc4056f9513db/aiohttp-3.14.1-cp314-cp314-android_24_arm64_v8a.whl", hash = "sha256:7c106c26852ca1c2047c6b80384f17100b4e439af276f21ef3d4e2f450ae7e15", size = 508531, upload-time = "2026-06-07T21:08:02.093Z" }, + { url = "https://files.pythonhosted.org/packages/fa/2e/bfa02f699d87ffc86d5959270b28f1cb410add3ccaced8ed2e0b8a5238fc/aiohttp-3.14.1-cp314-cp314-android_24_x86_64.whl", hash = "sha256:20205f7f5ade7aaec9f4b500549bbc071b046453aed72f9c06dcab87896a83e8", size = 514718, upload-time = "2026-06-07T21:08:04.476Z" }, + { url = "https://files.pythonhosted.org/packages/85/a5/9594ad6289eebbc97d167c44213d557807f90e59115caad24de21ad2c3b1/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:62a759436b29e677181a9e76bab8b8f689a29cb9c535f45f7c48c9c830d3f8c3", size = 487918, upload-time = "2026-06-07T21:08:06.377Z" }, + { url = "https://files.pythonhosted.org/packages/b4/61/16a32c36c3c49edec122a3dc811f2057df2f94d3b14aa107c8017d981618/aiohttp-3.14.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:2964cbf553df4d7a57348da44d961d871895fc1ee4e8c322b2a95612c7b17fba", size = 494014, upload-time = "2026-06-07T21:08:08.263Z" }, + { url = "https://files.pythonhosted.org/packages/9b/89/3ebcf96ed99c05bec9c434aaac6963fd3cbab4a786ae739908a144d9ce44/aiohttp-3.14.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:237651caadc3a59badd39319c54642b5299e9cc98a3a194310e55d5bb9f5e397", size = 502398, upload-time = "2026-06-07T21:08:10.244Z" }, + { url = "https://files.pythonhosted.org/packages/fd/3d/b74870a0c2d40c355928cd5b96c7a11fa821b8a40fc41365e64479b151fb/aiohttp-3.14.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:896e12dfdbbab9d8f7e16d2b28c6769a60126fa92095d1ebf9473d02593a2448", size = 758018, upload-time = "2026-06-07T21:08:12.447Z" }, + { url = "https://files.pythonhosted.org/packages/d3/66/f42f5c984d99e49c6cff5f26f590750f2e2f7ef1fcfb99966ab5be1b632e/aiohttp-3.14.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d03f281ed22579314ba00821ce20115a7c0ac430660b4cc05704a3f818b3e004", size = 512462, upload-time = "2026-06-07T21:08:14.624Z" }, + { url = "https://files.pythonhosted.org/packages/e9/a7/248e1aebe0c7810b0271e021a0f2a5eb6e78a051885b3c9df49f42a5802d/aiohttp-3.14.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:07eabb979d236335fed927e137a928c9adfb7df3b9ec7aa31726f133a62be983", size = 512824, upload-time = "2026-06-07T21:08:16.572Z" }, + { url = "https://files.pythonhosted.org/packages/26/97/2aa0e5ba0727dc3bd5aaebb7ccbc510f7dfb7fb961ec87497cd496635ab1/aiohttp-3.14.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4fe1f1087cbadb280b5e1bb054a4f00d1423c74d6626c5e48400d871d34ecefe", size = 1749898, upload-time = "2026-06-07T21:08:18.635Z" }, + { url = "https://files.pythonhosted.org/packages/00/8d/e97f6c96c891d457c8479d92a514ba194d0412f981d72c70341ee18488ed/aiohttp-3.14.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:367a9314fdc79dab0fac96e216cb41dd73c85bdca85306ce8999118ba7e0f333", size = 1710114, upload-time = "2026-06-07T21:08:20.892Z" }, + { url = "https://files.pythonhosted.org/packages/6f/e6/aa8d7e863048c8fceb5cd6ce74017311cec3ead07847387e12265fb4444e/aiohttp-3.14.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a24f677ebe83749039e7bdf862ff0bbb16818ae4193d4ef96505e269375bcce0", size = 1802541, upload-time = "2026-06-07T21:08:23.044Z" }, + { url = "https://files.pythonhosted.org/packages/83/a8/72193137de57fda4ebfae4563182d082c8856e3b6e9871d0b46f028fb369/aiohttp-3.14.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c83afe0ba876be7e943d2e0ba645809ad441575d2840c895c21ee5de93b9377a", size = 1875776, upload-time = "2026-06-07T21:08:25.288Z" }, + { url = "https://files.pythonhosted.org/packages/a0/18/938441025db6769a3464596b2410af3afde0b21eb2f204c6f766f68af4bd/aiohttp-3.14.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:634e385930fb6d2d479cf3aa66515955863b77a5e3c2b5894ca259a25b308602", size = 1760329, upload-time = "2026-06-07T21:08:27.363Z" }, + { url = "https://files.pythonhosted.org/packages/60/29/bf2496b4065e76e09fe48015aaffe5ce161d8f089b06ac6982070f653076/aiohttp-3.14.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeea07c4397bbc57719c4eed8f9c284874d4f175f9b6d57f7a1546b976d455ca", size = 1587293, upload-time = "2026-06-07T21:08:29.805Z" }, + { url = "https://files.pythonhosted.org/packages/49/a2/2136674d52123b1354bd05dd5753c318db47dc0c927cc70b27bab3755456/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:335c0cc3e3545ce98dcb9cfcb836f40c3411f43fa03dab757597d80c89af8a35", size = 1714756, upload-time = "2026-06-07T21:08:32.094Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b9/e5fd2e6f915503081c0f9b1e8540947037929c70c191da2e4d54b31a21a1/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:ae6be797afdef264e8a84864a85b196ca06045586481b3df8a967322fd2fa844", size = 1721052, upload-time = "2026-06-07T21:08:34.167Z" }, + { url = "https://files.pythonhosted.org/packages/63/5a/2833e324a2263e104e31e2e91bc5bbee81bc499afd32203faee048a883f0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:8560b4d712474335d08907db7973f71912d3a9a8f1dee992ec06b5d2fe359496", size = 1766888, upload-time = "2026-06-07T21:08:36.95Z" }, + { url = "https://files.pythonhosted.org/packages/57/fa/dea6511870913162f3b2e8c42a7614eb203a4540b8c2da43e0bfb0548f3c/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7edd08e0a5deb1e8564a2fcd8f4561014a3f05252334671bbf55ddd47db0e5", size = 1581679, upload-time = "2026-06-07T21:08:39.292Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/3cf0d55e71784b33534e9710a67d382d900598b4787fbce6cc7317f8c42a/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:b6ff7fcee63287ae57b5df3e4f5957ce032122802509246dec1a5bcc55904c95", size = 1782021, upload-time = "2026-06-07T21:08:41.407Z" }, + { url = "https://files.pythonhosted.org/packages/c1/af/14bb5843eccbe234f4dfb78ab73e549d99727247e62ae5d62cbd22eaf5b0/aiohttp-3.14.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ffbb2f4ec1ceaff7e07d43922954da26b223d188bf30658e561b98e23089444", size = 1742574, upload-time = "2026-06-07T21:08:43.795Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1e/fbeb7af9210a67ac0f9c9bec0f8f4568497924e33137a3d5b48e1cf85f3f/aiohttp-3.14.1-cp314-cp314-win32.whl", hash = "sha256:a9875b46d910cff3ea2f5962f9d266b465459fe634e22556ab9bd6fc1192eea0", size = 457773, upload-time = "2026-06-07T21:08:46.168Z" }, + { url = "https://files.pythonhosted.org/packages/f0/2b/13e8d741a9ec5db7d900c060554cf8352ab85e44e2a4469ebb9d377bda17/aiohttp-3.14.1-cp314-cp314-win_amd64.whl", hash = "sha256:af8b4b81a960eeaf1234971ac3cd0ba5901f3cd42eae42a46b4d089a8b492719", size = 485001, upload-time = "2026-06-07T21:08:48.401Z" }, + { url = "https://files.pythonhosted.org/packages/df/30/491acfa2c4d6c3ff59c49a14fc1b50be3241e25bbb0c84c09e2da4d11395/aiohttp-3.14.1-cp314-cp314-win_arm64.whl", hash = "sha256:cf4491381b1b57425c315a56a439251b1bdac07b2275f19a8c44bc57744532ec", size = 453809, upload-time = "2026-06-07T21:08:50.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/e3/19dbe1a1f4cc6230eb9e314de7fe68053b0992f9302b27d12141a0b5db53/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:819c054312f1af92947e6a55883d1b66feefab11531a7fc45e0fb9b63880b5c2", size = 793320, upload-time = "2026-06-07T21:08:52.775Z" }, + { url = "https://files.pythonhosted.org/packages/7f/20/1b7182219ba1b108430d6e4dc53d25ae02dcfcf5a045b33af4e8c5167527/aiohttp-3.14.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:10ee9c1753a8f706345b22496c79fbddb5be0599e0823f3738b1534058e25340", size = 529077, upload-time = "2026-06-07T21:08:55Z" }, + { url = "https://files.pythonhosted.org/packages/b9/c8/14ce60ec31a2e5f5274bb17d383a6f7a3aabca31ac04eee05585bbadab16/aiohttp-3.14.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1601cc37baf5750ccacae618ec2daf020769581695550e3b654a911f859c563d", size = 532476, upload-time = "2026-06-07T21:08:57.176Z" }, + { url = "https://files.pythonhosted.org/packages/7e/02/9ac85e081e53da2e061b02fa7758fe0a12d17b8ce2d1f5e6c7cb76730328/aiohttp-3.14.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4d6e0ac9da31c9c04c84e1c0182ad8d6df35965a85cae29cd71d089621b3ae94", size = 1922347, upload-time = "2026-06-07T21:08:59.563Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3e/d3ba07a0ab38b5389e10bec4362d21e10a4f667cba2d79ba30837b3a5059/aiohttp-3.14.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:9e8f2d660c350b3d0e259c7a7e3d9b7fc8b41210cbcc3d4a7076ff0a5e5c2fdc", size = 1786465, upload-time = "2026-06-07T21:09:01.909Z" }, + { url = "https://files.pythonhosted.org/packages/0b/cb/e2ee978a00cfb2df829704a69528b18154eba5939f45bc1efa8f33aee4c5/aiohttp-3.14.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4691802dda97be727f79d86818acaad7eb8e9252626a1d6b519fedbb92d5e251", size = 1909423, upload-time = "2026-06-07T21:09:04.357Z" }, + { url = "https://files.pythonhosted.org/packages/73/5d/1430334858b1022b58ae50399a918f0bd6fe8fa7fa183598d657ff61e040/aiohttp-3.14.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c389c482a7e9b9dc3ee2701ac46c4125297a3818875b9c305ddb603c04828fd1", size = 2001906, upload-time = "2026-06-07T21:09:06.722Z" }, + { url = "https://files.pythonhosted.org/packages/66/4e/560c7472d3d198a23aa5c8b19a5115bf6a9b77b7d3e4bb363da320430ad2/aiohttp-3.14.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fc0cacab7ba4e56f0f81c82a98c09bed2f39c940107b03a34b168bdf7597edd3", size = 1877095, upload-time = "2026-06-07T21:09:09.011Z" }, + { url = "https://files.pythonhosted.org/packages/0d/f1/4745806578d447db4a784a8591e2dae3afdfc2bcb96f8f81271b13df6543/aiohttp-3.14.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:979ed4717f59b8bb12e3963378fa285d93d367e15bcd66c721311826d3c44a6c", size = 1676222, upload-time = "2026-06-07T21:09:11.461Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c9/48255813cca749a229ef0ab476004ec623728ad79a9c0840616f6c076325/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:38e1e7daaea81df51c952e18483f323d878499a1e2bfe564790e0f9701d6f203", size = 1842922, upload-time = "2026-06-07T21:09:14.118Z" }, + { url = "https://files.pythonhosted.org/packages/3d/c0/bbd054e2bee909f529523a5af3891052606af5143c09f5f183ec3b234676/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:4132e72c608fe9fecb8f409113567605915b83e9bdd3ea56538d2f9cd35002f1", size = 1825035, upload-time = "2026-06-07T21:09:16.447Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ae/90395d4376deceb74e09ec26b6adf7d2015a6f8802d6d84446af860fef04/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:eefd9cc9b6d4a2db5f00a26bc3e4f9acf71926a6ec557cd56c9c6f27c290b665", size = 1849512, upload-time = "2026-06-07T21:09:18.742Z" }, + { url = "https://files.pythonhosted.org/packages/93/bd/fb25f3049957553d4ce0ba6ae480aa2f592a6985497fca590837d16c1be0/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b165790117eea512d7f3fb22f1f6dad3d55a7189571993eb015591c1401276d1", size = 1668571, upload-time = "2026-06-07T21:09:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/3f/22/7f73303d64dd567ff3addca90b556690ed1233a47b8f55d242fb90af3681/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:ed09c7eb1c391271c2ed0314a51903e72a3acb653d5ccfc264cdf3ef11f8269d", size = 1881159, upload-time = "2026-06-07T21:09:23.813Z" }, + { url = "https://files.pythonhosted.org/packages/44/be/0474c5a8b5640e1e4aa1923430a91f4151be82e511373fe764189b89aef5/aiohttp-3.14.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:99abd37084b82f5830c635fddd0b4993b9742a66eb746dacf433c8590e8f9e3c", size = 1841409, upload-time = "2026-06-07T21:09:26.207Z" }, + { url = "https://files.pythonhosted.org/packages/7b/3c/bb4a7cba26956cb3da4553cc2056cf67be5b5ff6e6d8fa4fbdff73bfb7ae/aiohttp-3.14.1-cp314-cp314t-win32.whl", hash = "sha256:47ddf841cdecc810749921d25606dee45857d12d2ad5ddb7b5bd7eab12e4b365", size = 494166, upload-time = "2026-06-07T21:09:28.505Z" }, + { url = "https://files.pythonhosted.org/packages/8a/84/ec80c2c1f66a952555a9f86df6b33af65108a6febfa0471b69013a12f807/aiohttp-3.14.1-cp314-cp314t-win_amd64.whl", hash = "sha256:5e78b522b7a6e27e0b25d19b247b75039ac4c94f99823e3c9e53ae1603a9f7e9", size = 530255, upload-time = "2026-06-07T21:09:30.843Z" }, + { url = "https://files.pythonhosted.org/packages/2a/71/6e22be134a4061ada85a92951b842f2657f17d926b727f3f94c56ae963d6/aiohttp-3.14.1-cp314-cp314t-win_arm64.whl", hash = "sha256:90d53f1609c29ccc2193945ef732428382a28f78d0456ae4d3daf0d48b74f0f6", size = 469640, upload-time = "2026-06-07T21:09:33.028Z" }, +] + +[[package]] +name = "aiosignal" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "frozenlist" }, + { name = "typing-extensions", marker = "python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/61/62/06741b579156360248d1ec624842ad0edf697050bbaf7c3e46394e106ad1/aiosignal-1.4.0.tar.gz", hash = "sha256:f47eecd9468083c2029cc99945502cb7708b082c232f9aca65da147157b251c7", size = 25007, upload-time = "2025-07-03T22:54:43.528Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fb/76/641ae371508676492379f16e2fa48f4e2c11741bd63c48be4b12a6b09cba/aiosignal-1.4.0-py3-none-any.whl", hash = "sha256:053243f8b92b990551949e63930a839ff0cf0b0ebbe0597b0f3fb19e1a0fe82e", size = 7490, upload-time = "2025-07-03T22:54:42.156Z" }, +] + +[[package]] +name = "annotated-types" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" }, +] + +[[package]] +name = "asttokens" +version = "3.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/be/a5/8e3f9b6771b0b408517c82d97aed8f2036509bc247d46114925e32fe33f0/asttokens-3.0.1.tar.gz", hash = "sha256:71a4ee5de0bde6a31d64f6b13f2293ac190344478f081c3d1bccfcf5eacb0cb7", size = 62308, upload-time = "2025-11-15T16:43:48.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d2/39/e7eaf1799466a4aef85b6a4fe7bd175ad2b1c6345066aa33f1f58d4b18d0/asttokens-3.0.1-py3-none-any.whl", hash = "sha256:15a3ebc0f43c2d0a50eeafea25e19046c68398e487b9f1f5b517f7c0f40f976a", size = 27047, upload-time = "2025-11-15T16:43:16.109Z" }, +] + +[[package]] +name = "async-timeout" +version = "5.0.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "auditwheel" +version = "6.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pyelftools", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/26/a0/8d998628f1d899420a621fcd7b46825cb96f23b5b52c2b93f4f814ae9c59/auditwheel-6.4.2.tar.gz", hash = "sha256:b7a61afc9183b6b5c661de59ca586f9c7200445a409c58cdf2049d6f71636d51", size = 4694856, upload-time = "2025-07-27T07:14:33.527Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d6/66/d790dc341d93c80e1ee61f2580177d7ee2e3e16dc100c36889b8bd176acf/auditwheel-6.4.2-py3-none-any.whl", hash = "sha256:4302ae79dcff242e799a37173cfeeae727d0924843eca4b3f622d3bcb28de2db", size = 50876, upload-time = "2025-07-27T07:13:27.565Z" }, +] + +[[package]] +name = "auditwheel" +version = "6.6.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pyelftools", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7b/ed/342df5a75589103d72402dcdd88d7b8cc2df338fbc39042ce1b3ac008960/auditwheel-6.6.0.tar.gz", hash = "sha256:277f3b315ad0b04df0a2be2d126c3fd39930bc265df0f9589d78c970ff06f52b", size = 4663481, upload-time = "2026-01-04T14:34:21.577Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b0/c2/bb8ba1cec816fa356e19da83c59bfd4ac34dba1ae41eb7622c18f4b1d48d/auditwheel-6.6.0-py3-none-any.whl", hash = "sha256:3e8479856ca582625b5457204cbb62d1614d3bc7e760ee307c04c34c912ebcad", size = 59184, upload-time = "2026-01-04T14:34:19.932Z" }, +] + +[[package]] +name = "backports-asyncio-runner" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8e/ff/70dca7d7cb1cbc0edb2c6cc0c38b65cba36cccc491eca64cabd5fe7f8670/backports_asyncio_runner-1.2.0.tar.gz", hash = "sha256:a5aa7b2b7d8f8bfcaa2b57313f70792df84e32a2a746f585213373f900b42162", size = 69893, upload-time = "2025-07-02T02:27:15.685Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/59/76ab57e3fe74484f48a53f8e337171b4a2349e506eabe136d7e01d059086/backports_asyncio_runner-1.2.0-py3-none-any.whl", hash = "sha256:0da0a936a8aeb554eccb426dc55af3ba63bcdc69fa1a600b5bb305413a4477b5", size = 12313, upload-time = "2025-07-02T02:27:14.263Z" }, +] + +[[package]] +name = "backports-tarfile" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/72/cd9b395f25e290e633655a100af28cb253e4393396264a98bd5f5951d50f/backports_tarfile-1.2.0.tar.gz", hash = "sha256:d75e02c268746e1b8144c278978b6e98e85de6ad16f8e4b0844a154557eca991", size = 86406, upload-time = "2024-05-28T17:01:54.731Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b9/fa/123043af240e49752f1c4bd24da5053b6bd00cad78c2be53c0d1e8b975bc/backports.tarfile-1.2.0-py3-none-any.whl", hash = "sha256:77e284d754527b01fb1e6fa8a1afe577858ebe4e9dad8919e34c862cb399bc34", size = 30181, upload-time = "2024-05-28T17:01:53.112Z" }, +] + +[[package]] +name = "bashlex" +version = "0.18" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/76/60/aae0bb54f9af5e0128ba90eb83d8d0d506ee8f0475c4fdda3deeda20b1d2/bashlex-0.18.tar.gz", hash = "sha256:5bb03a01c6d5676338c36fd1028009c8ad07e7d61d8a1ce3f513b7fff52796ee", size = 68742, upload-time = "2023-01-18T15:21:26.402Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/be/6985abb1011fda8a523cfe21ed9629e397d6e06fb5bae99750402b25c95b/bashlex-0.18-py2.py3-none-any.whl", hash = "sha256:91d73a23a3e51711919c1c899083890cdecffc91d8c088942725ac13e9dcfffa", size = 69539, upload-time = "2023-01-18T15:21:24.167Z" }, +] + +[[package]] +name = "bracex" +version = "2.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/63/9a/fec38644694abfaaeca2798b58e276a8e61de49e2e37494ace423395febc/bracex-2.6.tar.gz", hash = "sha256:98f1347cd77e22ee8d967a30ad4e310b233f7754dbf31ff3fceb76145ba47dc7", size = 26642, upload-time = "2025-06-22T19:12:31.254Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/2a/9186535ce58db529927f6cf5990a849aa9e052eea3e2cfefe20b9e1802da/bracex-2.6-py3-none-any.whl", hash = "sha256:0b0049264e7340b3ec782b5cb99beb325f36c3782a32e36e876452fd49a09952", size = 11508, upload-time = "2025-06-22T19:12:29.781Z" }, +] + +[[package]] +name = "build" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and os_name == 'nt'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "pyproject-hooks", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/18/94eaffda7b329535d91f00fe605ab1f1e5cd68b2074d03f255c7d250687d/build-1.4.0.tar.gz", hash = "sha256:f1b91b925aa322be454f8330c6fb48b465da993d1e7e7e6fa35027ec49f3c936", size = 50054, upload-time = "2026-01-08T16:41:47.696Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/0d/84a4380f930db0010168e0aa7b7a8fed9ba1835a8fbb1472bc6d0201d529/build-1.4.0-py3-none-any.whl", hash = "sha256:6a07c1b8eb6f2b311b96fcbdbce5dab5fe637ffda0fd83c9cac622e927501596", size = 24141, upload-time = "2026-01-08T16:41:46.453Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", version = "2.23", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and implementation_name != 'PyPy'" }, + { name = "pycparser", version = "3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/50/bd/b1a6362b80628111e6653c961f987faa55262b4002fcec42308cad1db680/cffi-2.0.0-cp310-cp310-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:53f77cbe57044e88bbd5ed26ac1d0514d2acf0591dd6bb02a3ae37f76811b80c", size = 208811, upload-time = "2025-09-08T23:22:12.267Z" }, + { url = "https://files.pythonhosted.org/packages/4f/27/6933a8b2562d7bd1fb595074cf99cc81fc3789f6a6c05cdabb46284a3188/cffi-2.0.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3e837e369566884707ddaf85fc1744b47575005c0a229de3327f8f9a20f4efeb", size = 216402, upload-time = "2025-09-08T23:22:13.455Z" }, + { url = "https://files.pythonhosted.org/packages/98/29/9b366e70e243eb3d14a5cb488dfd3a0b6b2f1fb001a203f653b93ccfac88/cffi-2.0.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc7de24befaeae77ba923797c7c87834c73648a05a4bde34b3b7e5588973a453", size = 216475, upload-time = "2025-09-08T23:22:17.427Z" }, + { url = "https://files.pythonhosted.org/packages/21/7a/13b24e70d2f90a322f2900c5d8e1f14fa7e2a6b3332b7309ba7b2ba51a5a/cffi-2.0.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cf364028c016c03078a23b503f02058f1814320a56ad535686f90565636a9495", size = 218829, upload-time = "2025-09-08T23:22:19.069Z" }, + { url = "https://files.pythonhosted.org/packages/60/99/c9dc110974c59cc981b1f5b66e1d8af8af764e00f0293266824d9c4254bc/cffi-2.0.0-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:e11e82b744887154b182fd3e7e8512418446501191994dbf9c9fc1f32cc8efd5", size = 211211, upload-time = "2025-09-08T23:22:20.588Z" }, + { url = "https://files.pythonhosted.org/packages/49/72/ff2d12dbf21aca1b32a40ed792ee6b40f6dc3a9cf1644bd7ef6e95e0ac5e/cffi-2.0.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8ea985900c5c95ce9db1745f7933eeef5d314f0565b27625d9a10ec9881e1bfb", size = 218036, upload-time = "2025-09-08T23:22:22.143Z" }, + { url = "https://files.pythonhosted.org/packages/b1/b7/1200d354378ef52ec227395d95c2576330fd22a869f7a70e88e1447eb234/cffi-2.0.0-cp311-cp311-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:baf5215e0ab74c16e2dd324e8ec067ef59e41125d3eade2b863d294fd5035c92", size = 209613, upload-time = "2025-09-08T23:22:29.475Z" }, + { url = "https://files.pythonhosted.org/packages/b8/56/6033f5e86e8cc9bb629f0077ba71679508bdf54a9a5e112a3c0b91870332/cffi-2.0.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:730cacb21e1bdff3ce90babf007d0a0917cc3e6492f336c2f0134101e0944f93", size = 216476, upload-time = "2025-09-08T23:22:31.063Z" }, + { url = "https://files.pythonhosted.org/packages/d7/91/500d892b2bf36529a75b77958edfcd5ad8e2ce4064ce2ecfeab2125d72d1/cffi-2.0.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8941aaadaf67246224cee8c3803777eed332a19d909b47e29c9842ef1e79ac26", size = 215574, upload-time = "2025-09-08T23:22:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/44/64/58f6255b62b101093d5df22dcb752596066c7e89dd725e0afaed242a61be/cffi-2.0.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:a05d0c237b3349096d3981b727493e22147f934b20f6f125a3eba8f994bec4a9", size = 218971, upload-time = "2025-09-08T23:22:36.805Z" }, + { url = "https://files.pythonhosted.org/packages/ab/49/fa72cebe2fd8a55fbe14956f9970fe8eb1ac59e5df042f603ef7c8ba0adc/cffi-2.0.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:94698a9c5f91f9d138526b48fe26a199609544591f859c870d477351dc7b2414", size = 211972, upload-time = "2025-09-08T23:22:38.436Z" }, + { url = "https://files.pythonhosted.org/packages/0b/28/dd0967a76aab36731b6ebfe64dec4e981aff7e0608f60c2d46b46982607d/cffi-2.0.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:5fed36fccc0612a53f1d4d9a816b50a36702c28a2aa880cb8a122b3466638743", size = 217078, upload-time = "2025-09-08T23:22:39.776Z" }, + { url = "https://files.pythonhosted.org/packages/ff/df/a4f0fbd47331ceeba3d37c2e51e9dfc9722498becbeec2bd8bc856c9538a/cffi-2.0.0-cp312-cp312-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:21d1152871b019407d8ac3985f6775c079416c282e431a4da6afe7aefd2bccbe", size = 212529, upload-time = "2025-09-08T23:22:47.349Z" }, + { url = "https://files.pythonhosted.org/packages/d5/72/12b5f8d3865bf0f87cf1404d8c374e7487dcf097a1c91c436e72e6badd83/cffi-2.0.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b21e08af67b8a103c71a250401c78d5e0893beff75e28c53c98f4de42f774062", size = 220097, upload-time = "2025-09-08T23:22:48.677Z" }, + { url = "https://files.pythonhosted.org/packages/78/2d/7fa73dfa841b5ac06c7b8855cfc18622132e365f5b81d02230333ff26e9e/cffi-2.0.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3e17ed538242334bf70832644a32a7aae3d83b57567f9fd60a26257e992b79ba", size = 219572, upload-time = "2025-09-08T23:22:52.902Z" }, + { url = "https://files.pythonhosted.org/packages/07/e0/267e57e387b4ca276b90f0434ff88b2c2241ad72b16d31836adddfd6031b/cffi-2.0.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3925dd22fa2b7699ed2617149842d2e6adde22b262fcbfada50e3d195e4b3a94", size = 222963, upload-time = "2025-09-08T23:22:54.518Z" }, + { url = "https://files.pythonhosted.org/packages/b6/75/1f2747525e06f53efbd878f4d03bac5b859cbc11c633d0fb81432d98a795/cffi-2.0.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:2c8f814d84194c9ea681642fd164267891702542f028a15fc97d4674b6206187", size = 221361, upload-time = "2025-09-08T23:22:55.867Z" }, + { url = "https://files.pythonhosted.org/packages/b0/1e/d22cc63332bd59b06481ceaac49d6c507598642e2230f201649058a7e704/cffi-2.0.0-cp313-cp313-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:07b271772c100085dd28b74fa0cd81c8fb1a3ba18b21e03d7c27f3436a10606b", size = 212446, upload-time = "2025-09-08T23:23:03.472Z" }, + { url = "https://files.pythonhosted.org/packages/a9/f5/a2c23eb03b61a0b8747f211eb716446c826ad66818ddc7810cc2cc19b3f2/cffi-2.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d48a880098c96020b02d5a1f7d9251308510ce8858940e6fa99ece33f610838b", size = 220101, upload-time = "2025-09-08T23:23:04.792Z" }, + { url = "https://files.pythonhosted.org/packages/98/df/0a1755e750013a2081e863e7cd37e0cdd02664372c754e5560099eb7aa44/cffi-2.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8d3b5532fc71b7a77c09192b4a5a200ea992702734a2e9279a37f2478236f26", size = 219499, upload-time = "2025-09-08T23:23:09.648Z" }, + { url = "https://files.pythonhosted.org/packages/50/e1/a969e687fcf9ea58e6e2a928ad5e2dd88cc12f6f0ab477e9971f2309b57c/cffi-2.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:d9b29c1f0ae438d5ee9acb31cadee00a58c46cc9c0b2f9038c6b0b3470877a8c", size = 222928, upload-time = "2025-09-08T23:23:10.928Z" }, + { url = "https://files.pythonhosted.org/packages/36/54/0362578dd2c9e557a28ac77698ed67323ed5b9775ca9d3fe73fe191bb5d8/cffi-2.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6d50360be4546678fc1b79ffe7a66265e28667840010348dd69a314145807a1b", size = 221302, upload-time = "2025-09-08T23:23:12.42Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/9b/13/c92e36358fbcc39cf0962e83223c9522154ee8630e1df7c0b3a39a8124e2/cffi-2.0.0-cp39-cp39-manylinux1_i686.manylinux2014_i686.manylinux_2_17_i686.manylinux_2_5_i686.whl", hash = "sha256:4647afc2f90d1ddd33441e5b0e85b16b12ddec4fca55f0d9671fef036ecca27c", size = 208813, upload-time = "2025-09-08T23:23:51.263Z" }, + { url = "https://files.pythonhosted.org/packages/15/12/a7a79bd0df4c3bff744b2d7e52cc1b68d5e7e427b384252c42366dc1ecbc/cffi-2.0.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:3f4d46d8b35698056ec29bca21546e1551a205058ae1a181d871e278b0b28165", size = 216498, upload-time = "2025-09-08T23:23:52.494Z" }, + { url = "https://files.pythonhosted.org/packages/1f/74/cc4096ce66f5939042ae094e2e96f53426a979864aa1f96a621ad128be27/cffi-2.0.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:61d028e90346df14fedc3d1e5441df818d095f3b87d286825dfcbd6459b7ef63", size = 216548, upload-time = "2025-09-08T23:23:56.506Z" }, + { url = "https://files.pythonhosted.org/packages/e8/be/f6424d1dc46b1091ffcc8964fa7c0ab0cd36839dd2761b49c90481a6ba1b/cffi-2.0.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:0f6084a0ea23d05d20c3edcda20c3d006f9b6f3fefeac38f59262e10cef47ee2", size = 218897, upload-time = "2025-09-08T23:23:57.825Z" }, + { url = "https://files.pythonhosted.org/packages/f7/e0/dda537c2309817edf60109e39265f24f24aa7f050767e22c98c53fe7f48b/cffi-2.0.0-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:1cd13c99ce269b3ed80b417dcd591415d3372bcac067009b6e0f59c7d4015e65", size = 211249, upload-time = "2025-09-08T23:23:59.139Z" }, + { url = "https://files.pythonhosted.org/packages/2b/e7/7c769804eb75e4c4b35e658dba01de1640a351a9653c3d49ca89d16ccc91/cffi-2.0.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:89472c9762729b5ae1ad974b777416bfda4ac5642423fa93bd57a09204712322", size = 218041, upload-time = "2025-09-08T23:24:00.496Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/13/69/33ddede1939fdd074bce5434295f38fae7136463422fe4fd3e0e89b98062/charset_normalizer-3.4.4.tar.gz", hash = "sha256:94537985111c35f28720e43603b8e7b43a6ecfb2ce1d3058bbe955b73404e21a", size = 129418, upload-time = "2025-10-14T04:42:32.879Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/b8/6d51fc1d52cbd52cd4ccedd5b5b2f0f6a11bbf6765c782298b0f3e808541/charset_normalizer-3.4.4-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:e824f1492727fa856dd6eda4f7cee25f8518a12f3c4a56a74e8095695089cf6d", size = 209709, upload-time = "2025-10-14T04:40:11.385Z" }, + { url = "https://files.pythonhosted.org/packages/5c/af/1f9d7f7faafe2ddfb6f72a2e07a548a629c61ad510fe60f9630309908fef/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4bd5d4137d500351a30687c2d3971758aac9a19208fc110ccb9d7188fbe709e8", size = 148814, upload-time = "2025-10-14T04:40:13.135Z" }, + { url = "https://files.pythonhosted.org/packages/79/3d/f2e3ac2bbc056ca0c204298ea4e3d9db9b4afe437812638759db2c976b5f/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:027f6de494925c0ab2a55eab46ae5129951638a49a34d87f4c3eda90f696b4ad", size = 144467, upload-time = "2025-10-14T04:40:14.728Z" }, + { url = "https://files.pythonhosted.org/packages/ec/85/1bf997003815e60d57de7bd972c57dc6950446a3e4ccac43bc3070721856/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f820802628d2694cb7e56db99213f930856014862f3fd943d290ea8438d07ca8", size = 162280, upload-time = "2025-10-14T04:40:16.14Z" }, + { url = "https://files.pythonhosted.org/packages/3e/8e/6aa1952f56b192f54921c436b87f2aaf7c7a7c3d0d1a765547d64fd83c13/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:798d75d81754988d2565bff1b97ba5a44411867c0cf32b77a7e8f8d84796b10d", size = 159454, upload-time = "2025-10-14T04:40:17.567Z" }, + { url = "https://files.pythonhosted.org/packages/36/3b/60cbd1f8e93aa25d1c669c649b7a655b0b5fb4c571858910ea9332678558/charset_normalizer-3.4.4-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d1bb833febdff5c8927f922386db610b49db6e0d4f4ee29601d71e7c2694313", size = 153609, upload-time = "2025-10-14T04:40:19.08Z" }, + { url = "https://files.pythonhosted.org/packages/64/91/6a13396948b8fd3c4b4fd5bc74d045f5637d78c9675585e8e9fbe5636554/charset_normalizer-3.4.4-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9cd98cdc06614a2f768d2b7286d66805f94c48cde050acdbbb7db2600ab3197e", size = 151849, upload-time = "2025-10-14T04:40:20.607Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7a/59482e28b9981d105691e968c544cc0df3b7d6133152fb3dcdc8f135da7a/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:077fbb858e903c73f6c9db43374fd213b0b6a778106bc7032446a8e8b5b38b93", size = 151586, upload-time = "2025-10-14T04:40:21.719Z" }, + { url = "https://files.pythonhosted.org/packages/92/59/f64ef6a1c4bdd2baf892b04cd78792ed8684fbc48d4c2afe467d96b4df57/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:244bfb999c71b35de57821b8ea746b24e863398194a4014e4c76adc2bbdfeff0", size = 145290, upload-time = "2025-10-14T04:40:23.069Z" }, + { url = "https://files.pythonhosted.org/packages/6b/63/3bf9f279ddfa641ffa1962b0db6a57a9c294361cc2f5fcac997049a00e9c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:64b55f9dce520635f018f907ff1b0df1fdc31f2795a922fb49dd14fbcdf48c84", size = 163663, upload-time = "2025-10-14T04:40:24.17Z" }, + { url = "https://files.pythonhosted.org/packages/ed/09/c9e38fc8fa9e0849b172b581fd9803bdf6e694041127933934184e19f8c3/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:faa3a41b2b66b6e50f84ae4a68c64fcd0c44355741c6374813a800cd6695db9e", size = 151964, upload-time = "2025-10-14T04:40:25.368Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d1/d28b747e512d0da79d8b6a1ac18b7ab2ecfd81b2944c4c710e166d8dd09c/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:6515f3182dbe4ea06ced2d9e8666d97b46ef4c75e326b79bb624110f122551db", size = 161064, upload-time = "2025-10-14T04:40:26.806Z" }, + { url = "https://files.pythonhosted.org/packages/bb/9a/31d62b611d901c3b9e5500c36aab0ff5eb442043fb3a1c254200d3d397d9/charset_normalizer-3.4.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:cc00f04ed596e9dc0da42ed17ac5e596c6ccba999ba6bd92b0e0aef2f170f2d6", size = 155015, upload-time = "2025-10-14T04:40:28.284Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/107e008fa2bff0c8b9319584174418e5e5285fef32f79d8ee6a430d0039c/charset_normalizer-3.4.4-cp310-cp310-win32.whl", hash = "sha256:f34be2938726fc13801220747472850852fe6b1ea75869a048d6f896838c896f", size = 99792, upload-time = "2025-10-14T04:40:29.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/66/e396e8a408843337d7315bab30dbf106c38966f1819f123257f5520f8a96/charset_normalizer-3.4.4-cp310-cp310-win_amd64.whl", hash = "sha256:a61900df84c667873b292c3de315a786dd8dac506704dea57bc957bd31e22c7d", size = 107198, upload-time = "2025-10-14T04:40:30.644Z" }, + { url = "https://files.pythonhosted.org/packages/b5/58/01b4f815bf0312704c267f2ccb6e5d42bcc7752340cd487bc9f8c3710597/charset_normalizer-3.4.4-cp310-cp310-win_arm64.whl", hash = "sha256:cead0978fc57397645f12578bfd2d5ea9138ea0fac82b2f63f7f7c6877986a69", size = 100262, upload-time = "2025-10-14T04:40:32.108Z" }, + { url = "https://files.pythonhosted.org/packages/ed/27/c6491ff4954e58a10f69ad90aca8a1b6fe9c5d3c6f380907af3c37435b59/charset_normalizer-3.4.4-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6e1fcf0720908f200cd21aa4e6750a48ff6ce4afe7ff5a79a90d5ed8a08296f8", size = 206988, upload-time = "2025-10-14T04:40:33.79Z" }, + { url = "https://files.pythonhosted.org/packages/94/59/2e87300fe67ab820b5428580a53cad894272dbb97f38a7a814a2a1ac1011/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f819d5fe9234f9f82d75bdfa9aef3a3d72c4d24a6e57aeaebba32a704553aa0", size = 147324, upload-time = "2025-10-14T04:40:34.961Z" }, + { url = "https://files.pythonhosted.org/packages/07/fb/0cf61dc84b2b088391830f6274cb57c82e4da8bbc2efeac8c025edb88772/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a59cb51917aa591b1c4e6a43c132f0cdc3c76dbad6155df4e28ee626cc77a0a3", size = 142742, upload-time = "2025-10-14T04:40:36.105Z" }, + { url = "https://files.pythonhosted.org/packages/62/8b/171935adf2312cd745d290ed93cf16cf0dfe320863ab7cbeeae1dcd6535f/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8ef3c867360f88ac904fd3f5e1f902f13307af9052646963ee08ff4f131adafc", size = 160863, upload-time = "2025-10-14T04:40:37.188Z" }, + { url = "https://files.pythonhosted.org/packages/09/73/ad875b192bda14f2173bfc1bc9a55e009808484a4b256748d931b6948442/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d9e45d7faa48ee908174d8fe84854479ef838fc6a705c9315372eacbc2f02897", size = 157837, upload-time = "2025-10-14T04:40:38.435Z" }, + { url = "https://files.pythonhosted.org/packages/6d/fc/de9cce525b2c5b94b47c70a4b4fb19f871b24995c728e957ee68ab1671ea/charset_normalizer-3.4.4-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:840c25fb618a231545cbab0564a799f101b63b9901f2569faecd6b222ac72381", size = 151550, upload-time = "2025-10-14T04:40:40.053Z" }, + { url = "https://files.pythonhosted.org/packages/55/c2/43edd615fdfba8c6f2dfbd459b25a6b3b551f24ea21981e23fb768503ce1/charset_normalizer-3.4.4-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ca5862d5b3928c4940729dacc329aa9102900382fea192fc5e52eb69d6093815", size = 149162, upload-time = "2025-10-14T04:40:41.163Z" }, + { url = "https://files.pythonhosted.org/packages/03/86/bde4ad8b4d0e9429a4e82c1e8f5c659993a9a863ad62c7df05cf7b678d75/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d9c7f57c3d666a53421049053eaacdd14bbd0a528e2186fcb2e672effd053bb0", size = 150019, upload-time = "2025-10-14T04:40:42.276Z" }, + { url = "https://files.pythonhosted.org/packages/1f/86/a151eb2af293a7e7bac3a739b81072585ce36ccfb4493039f49f1d3cae8c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:277e970e750505ed74c832b4bf75dac7476262ee2a013f5574dd49075879e161", size = 143310, upload-time = "2025-10-14T04:40:43.439Z" }, + { url = "https://files.pythonhosted.org/packages/b5/fe/43dae6144a7e07b87478fdfc4dbe9efd5defb0e7ec29f5f58a55aeef7bf7/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:31fd66405eaf47bb62e8cd575dc621c56c668f27d46a61d975a249930dd5e2a4", size = 162022, upload-time = "2025-10-14T04:40:44.547Z" }, + { url = "https://files.pythonhosted.org/packages/80/e6/7aab83774f5d2bca81f42ac58d04caf44f0cc2b65fc6db2b3b2e8a05f3b3/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:0d3d8f15c07f86e9ff82319b3d9ef6f4bf907608f53fe9d92b28ea9ae3d1fd89", size = 149383, upload-time = "2025-10-14T04:40:46.018Z" }, + { url = "https://files.pythonhosted.org/packages/4f/e8/b289173b4edae05c0dde07f69f8db476a0b511eac556dfe0d6bda3c43384/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:9f7fcd74d410a36883701fafa2482a6af2ff5ba96b9a620e9e0721e28ead5569", size = 159098, upload-time = "2025-10-14T04:40:47.081Z" }, + { url = "https://files.pythonhosted.org/packages/d8/df/fe699727754cae3f8478493c7f45f777b17c3ef0600e28abfec8619eb49c/charset_normalizer-3.4.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:ebf3e58c7ec8a8bed6d66a75d7fb37b55e5015b03ceae72a8e7c74495551e224", size = 152991, upload-time = "2025-10-14T04:40:48.246Z" }, + { url = "https://files.pythonhosted.org/packages/1a/86/584869fe4ddb6ffa3bd9f491b87a01568797fb9bd8933f557dba9771beaf/charset_normalizer-3.4.4-cp311-cp311-win32.whl", hash = "sha256:eecbc200c7fd5ddb9a7f16c7decb07b566c29fa2161a16cf67b8d068bd21690a", size = 99456, upload-time = "2025-10-14T04:40:49.376Z" }, + { url = "https://files.pythonhosted.org/packages/65/f6/62fdd5feb60530f50f7e38b4f6a1d5203f4d16ff4f9f0952962c044e919a/charset_normalizer-3.4.4-cp311-cp311-win_amd64.whl", hash = "sha256:5ae497466c7901d54b639cf42d5b8c1b6a4fead55215500d2f486d34db48d016", size = 106978, upload-time = "2025-10-14T04:40:50.844Z" }, + { url = "https://files.pythonhosted.org/packages/7a/9d/0710916e6c82948b3be62d9d398cb4fcf4e97b56d6a6aeccd66c4b2f2bd5/charset_normalizer-3.4.4-cp311-cp311-win_arm64.whl", hash = "sha256:65e2befcd84bc6f37095f5961e68a6f077bf44946771354a28ad434c2cce0ae1", size = 99969, upload-time = "2025-10-14T04:40:52.272Z" }, + { url = "https://files.pythonhosted.org/packages/f3/85/1637cd4af66fa687396e757dec650f28025f2a2f5a5531a3208dc0ec43f2/charset_normalizer-3.4.4-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0a98e6759f854bd25a58a73fa88833fba3b7c491169f86ce1180c948ab3fd394", size = 208425, upload-time = "2025-10-14T04:40:53.353Z" }, + { url = "https://files.pythonhosted.org/packages/9d/6a/04130023fef2a0d9c62d0bae2649b69f7b7d8d24ea5536feef50551029df/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b5b290ccc2a263e8d185130284f8501e3e36c5e02750fc6b6bdeb2e9e96f1e25", size = 148162, upload-time = "2025-10-14T04:40:54.558Z" }, + { url = "https://files.pythonhosted.org/packages/78/29/62328d79aa60da22c9e0b9a66539feae06ca0f5a4171ac4f7dc285b83688/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74bb723680f9f7a6234dcf67aea57e708ec1fbdf5699fb91dfd6f511b0a320ef", size = 144558, upload-time = "2025-10-14T04:40:55.677Z" }, + { url = "https://files.pythonhosted.org/packages/86/bb/b32194a4bf15b88403537c2e120b817c61cd4ecffa9b6876e941c3ee38fe/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f1e34719c6ed0b92f418c7c780480b26b5d9c50349e9a9af7d76bf757530350d", size = 161497, upload-time = "2025-10-14T04:40:57.217Z" }, + { url = "https://files.pythonhosted.org/packages/19/89/a54c82b253d5b9b111dc74aca196ba5ccfcca8242d0fb64146d4d3183ff1/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2437418e20515acec67d86e12bf70056a33abdacb5cb1655042f6538d6b085a8", size = 159240, upload-time = "2025-10-14T04:40:58.358Z" }, + { url = "https://files.pythonhosted.org/packages/c0/10/d20b513afe03acc89ec33948320a5544d31f21b05368436d580dec4e234d/charset_normalizer-3.4.4-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:11d694519d7f29d6cd09f6ac70028dba10f92f6cdd059096db198c283794ac86", size = 153471, upload-time = "2025-10-14T04:40:59.468Z" }, + { url = "https://files.pythonhosted.org/packages/61/fa/fbf177b55bdd727010f9c0a3c49eefa1d10f960e5f09d1d887bf93c2e698/charset_normalizer-3.4.4-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:ac1c4a689edcc530fc9d9aa11f5774b9e2f33f9a0c6a57864e90908f5208d30a", size = 150864, upload-time = "2025-10-14T04:41:00.623Z" }, + { url = "https://files.pythonhosted.org/packages/05/12/9fbc6a4d39c0198adeebbde20b619790e9236557ca59fc40e0e3cebe6f40/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:21d142cc6c0ec30d2efee5068ca36c128a30b0f2c53c1c07bd78cb6bc1d3be5f", size = 150647, upload-time = "2025-10-14T04:41:01.754Z" }, + { url = "https://files.pythonhosted.org/packages/ad/1f/6a9a593d52e3e8c5d2b167daf8c6b968808efb57ef4c210acb907c365bc4/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:5dbe56a36425d26d6cfb40ce79c314a2e4dd6211d51d6d2191c00bed34f354cc", size = 145110, upload-time = "2025-10-14T04:41:03.231Z" }, + { url = "https://files.pythonhosted.org/packages/30/42/9a52c609e72471b0fc54386dc63c3781a387bb4fe61c20231a4ebcd58bdd/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:5bfbb1b9acf3334612667b61bd3002196fe2a1eb4dd74d247e0f2a4d50ec9bbf", size = 162839, upload-time = "2025-10-14T04:41:04.715Z" }, + { url = "https://files.pythonhosted.org/packages/c4/5b/c0682bbf9f11597073052628ddd38344a3d673fda35a36773f7d19344b23/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:d055ec1e26e441f6187acf818b73564e6e6282709e9bcb5b63f5b23068356a15", size = 150667, upload-time = "2025-10-14T04:41:05.827Z" }, + { url = "https://files.pythonhosted.org/packages/e4/24/a41afeab6f990cf2daf6cb8c67419b63b48cf518e4f56022230840c9bfb2/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:af2d8c67d8e573d6de5bc30cdb27e9b95e49115cd9baad5ddbd1a6207aaa82a9", size = 160535, upload-time = "2025-10-14T04:41:06.938Z" }, + { url = "https://files.pythonhosted.org/packages/2a/e5/6a4ce77ed243c4a50a1fecca6aaaab419628c818a49434be428fe24c9957/charset_normalizer-3.4.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:780236ac706e66881f3b7f2f32dfe90507a09e67d1d454c762cf642e6e1586e0", size = 154816, upload-time = "2025-10-14T04:41:08.101Z" }, + { url = "https://files.pythonhosted.org/packages/a8/ef/89297262b8092b312d29cdb2517cb1237e51db8ecef2e9af5edbe7b683b1/charset_normalizer-3.4.4-cp312-cp312-win32.whl", hash = "sha256:5833d2c39d8896e4e19b689ffc198f08ea58116bee26dea51e362ecc7cd3ed26", size = 99694, upload-time = "2025-10-14T04:41:09.23Z" }, + { url = "https://files.pythonhosted.org/packages/3d/2d/1e5ed9dd3b3803994c155cd9aacb60c82c331bad84daf75bcb9c91b3295e/charset_normalizer-3.4.4-cp312-cp312-win_amd64.whl", hash = "sha256:a79cfe37875f822425b89a82333404539ae63dbdddf97f84dcbc3d339aae9525", size = 107131, upload-time = "2025-10-14T04:41:10.467Z" }, + { url = "https://files.pythonhosted.org/packages/d0/d9/0ed4c7098a861482a7b6a95603edce4c0d9db2311af23da1fb2b75ec26fc/charset_normalizer-3.4.4-cp312-cp312-win_arm64.whl", hash = "sha256:376bec83a63b8021bb5c8ea75e21c4ccb86e7e45ca4eb81146091b56599b80c3", size = 100390, upload-time = "2025-10-14T04:41:11.915Z" }, + { url = "https://files.pythonhosted.org/packages/97/45/4b3a1239bbacd321068ea6e7ac28875b03ab8bc0aa0966452db17cd36714/charset_normalizer-3.4.4-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:e1f185f86a6f3403aa2420e815904c67b2f9ebc443f045edd0de921108345794", size = 208091, upload-time = "2025-10-14T04:41:13.346Z" }, + { url = "https://files.pythonhosted.org/packages/7d/62/73a6d7450829655a35bb88a88fca7d736f9882a27eacdca2c6d505b57e2e/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6b39f987ae8ccdf0d2642338faf2abb1862340facc796048b604ef14919e55ed", size = 147936, upload-time = "2025-10-14T04:41:14.461Z" }, + { url = "https://files.pythonhosted.org/packages/89/c5/adb8c8b3d6625bef6d88b251bbb0d95f8205831b987631ab0c8bb5d937c2/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3162d5d8ce1bb98dd51af660f2121c55d0fa541b46dff7bb9b9f86ea1d87de72", size = 144180, upload-time = "2025-10-14T04:41:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/91/ed/9706e4070682d1cc219050b6048bfd293ccf67b3d4f5a4f39207453d4b99/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:81d5eb2a312700f4ecaa977a8235b634ce853200e828fbadf3a9c50bab278328", size = 161346, upload-time = "2025-10-14T04:41:16.738Z" }, + { url = "https://files.pythonhosted.org/packages/d5/0d/031f0d95e4972901a2f6f09ef055751805ff541511dc1252ba3ca1f80cf5/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5bd2293095d766545ec1a8f612559f6b40abc0eb18bb2f5d1171872d34036ede", size = 158874, upload-time = "2025-10-14T04:41:17.923Z" }, + { url = "https://files.pythonhosted.org/packages/f5/83/6ab5883f57c9c801ce5e5677242328aa45592be8a00644310a008d04f922/charset_normalizer-3.4.4-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a8a8b89589086a25749f471e6a900d3f662d1d3b6e2e59dcecf787b1cc3a1894", size = 153076, upload-time = "2025-10-14T04:41:19.106Z" }, + { url = "https://files.pythonhosted.org/packages/75/1e/5ff781ddf5260e387d6419959ee89ef13878229732732ee73cdae01800f2/charset_normalizer-3.4.4-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bc7637e2f80d8530ee4a78e878bce464f70087ce73cf7c1caf142416923b98f1", size = 150601, upload-time = "2025-10-14T04:41:20.245Z" }, + { url = "https://files.pythonhosted.org/packages/d7/57/71be810965493d3510a6ca79b90c19e48696fb1ff964da319334b12677f0/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f8bf04158c6b607d747e93949aa60618b61312fe647a6369f88ce2ff16043490", size = 150376, upload-time = "2025-10-14T04:41:21.398Z" }, + { url = "https://files.pythonhosted.org/packages/e5/d5/c3d057a78c181d007014feb7e9f2e65905a6c4ef182c0ddf0de2924edd65/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:554af85e960429cf30784dd47447d5125aaa3b99a6f0683589dbd27e2f45da44", size = 144825, upload-time = "2025-10-14T04:41:22.583Z" }, + { url = "https://files.pythonhosted.org/packages/e6/8c/d0406294828d4976f275ffbe66f00266c4b3136b7506941d87c00cab5272/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:74018750915ee7ad843a774364e13a3db91682f26142baddf775342c3f5b1133", size = 162583, upload-time = "2025-10-14T04:41:23.754Z" }, + { url = "https://files.pythonhosted.org/packages/d7/24/e2aa1f18c8f15c4c0e932d9287b8609dd30ad56dbe41d926bd846e22fb8d/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:c0463276121fdee9c49b98908b3a89c39be45d86d1dbaa22957e38f6321d4ce3", size = 150366, upload-time = "2025-10-14T04:41:25.27Z" }, + { url = "https://files.pythonhosted.org/packages/e4/5b/1e6160c7739aad1e2df054300cc618b06bf784a7a164b0f238360721ab86/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:362d61fd13843997c1c446760ef36f240cf81d3ebf74ac62652aebaf7838561e", size = 160300, upload-time = "2025-10-14T04:41:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/7a/10/f882167cd207fbdd743e55534d5d9620e095089d176d55cb22d5322f2afd/charset_normalizer-3.4.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a26f18905b8dd5d685d6d07b0cdf98a79f3c7a918906af7cc143ea2e164c8bc", size = 154465, upload-time = "2025-10-14T04:41:28.322Z" }, + { url = "https://files.pythonhosted.org/packages/89/66/c7a9e1b7429be72123441bfdbaf2bc13faab3f90b933f664db506dea5915/charset_normalizer-3.4.4-cp313-cp313-win32.whl", hash = "sha256:9b35f4c90079ff2e2edc5b26c0c77925e5d2d255c42c74fdb70fb49b172726ac", size = 99404, upload-time = "2025-10-14T04:41:29.95Z" }, + { url = "https://files.pythonhosted.org/packages/c4/26/b9924fa27db384bdcd97ab83b4f0a8058d96ad9626ead570674d5e737d90/charset_normalizer-3.4.4-cp313-cp313-win_amd64.whl", hash = "sha256:b435cba5f4f750aa6c0a0d92c541fb79f69a387c91e61f1795227e4ed9cece14", size = 107092, upload-time = "2025-10-14T04:41:31.188Z" }, + { url = "https://files.pythonhosted.org/packages/af/8f/3ed4bfa0c0c72a7ca17f0380cd9e4dd842b09f664e780c13cff1dcf2ef1b/charset_normalizer-3.4.4-cp313-cp313-win_arm64.whl", hash = "sha256:542d2cee80be6f80247095cc36c418f7bddd14f4a6de45af91dfad36d817bba2", size = 100408, upload-time = "2025-10-14T04:41:32.624Z" }, + { url = "https://files.pythonhosted.org/packages/2a/35/7051599bd493e62411d6ede36fd5af83a38f37c4767b92884df7301db25d/charset_normalizer-3.4.4-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:da3326d9e65ef63a817ecbcc0df6e94463713b754fe293eaa03da99befb9a5bd", size = 207746, upload-time = "2025-10-14T04:41:33.773Z" }, + { url = "https://files.pythonhosted.org/packages/10/9a/97c8d48ef10d6cd4fcead2415523221624bf58bcf68a802721a6bc807c8f/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8af65f14dc14a79b924524b1e7fffe304517b2bff5a58bf64f30b98bbc5079eb", size = 147889, upload-time = "2025-10-14T04:41:34.897Z" }, + { url = "https://files.pythonhosted.org/packages/10/bf/979224a919a1b606c82bd2c5fa49b5c6d5727aa47b4312bb27b1734f53cd/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:74664978bb272435107de04e36db5a9735e78232b85b77d45cfb38f758efd33e", size = 143641, upload-time = "2025-10-14T04:41:36.116Z" }, + { url = "https://files.pythonhosted.org/packages/ba/33/0ad65587441fc730dc7bd90e9716b30b4702dc7b617e6ba4997dc8651495/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:752944c7ffbfdd10c074dc58ec2d5a8a4cd9493b314d367c14d24c17684ddd14", size = 160779, upload-time = "2025-10-14T04:41:37.229Z" }, + { url = "https://files.pythonhosted.org/packages/67/ed/331d6b249259ee71ddea93f6f2f0a56cfebd46938bde6fcc6f7b9a3d0e09/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d1f13550535ad8cff21b8d757a3257963e951d96e20ec82ab44bc64aeb62a191", size = 159035, upload-time = "2025-10-14T04:41:38.368Z" }, + { url = "https://files.pythonhosted.org/packages/67/ff/f6b948ca32e4f2a4576aa129d8bed61f2e0543bf9f5f2b7fc3758ed005c9/charset_normalizer-3.4.4-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ecaae4149d99b1c9e7b88bb03e3221956f68fd6d50be2ef061b2381b61d20838", size = 152542, upload-time = "2025-10-14T04:41:39.862Z" }, + { url = "https://files.pythonhosted.org/packages/16/85/276033dcbcc369eb176594de22728541a925b2632f9716428c851b149e83/charset_normalizer-3.4.4-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cb6254dc36b47a990e59e1068afacdcd02958bdcce30bb50cc1700a8b9d624a6", size = 149524, upload-time = "2025-10-14T04:41:41.319Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f2/6a2a1f722b6aba37050e626530a46a68f74e63683947a8acff92569f979a/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:c8ae8a0f02f57a6e61203a31428fa1d677cbe50c93622b4149d5c0f319c1d19e", size = 150395, upload-time = "2025-10-14T04:41:42.539Z" }, + { url = "https://files.pythonhosted.org/packages/60/bb/2186cb2f2bbaea6338cad15ce23a67f9b0672929744381e28b0592676824/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:47cc91b2f4dd2833fddaedd2893006b0106129d4b94fdb6af1f4ce5a9965577c", size = 143680, upload-time = "2025-10-14T04:41:43.661Z" }, + { url = "https://files.pythonhosted.org/packages/7d/a5/bf6f13b772fbb2a90360eb620d52ed8f796f3c5caee8398c3b2eb7b1c60d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:82004af6c302b5d3ab2cfc4cc5f29db16123b1a8417f2e25f9066f91d4411090", size = 162045, upload-time = "2025-10-14T04:41:44.821Z" }, + { url = "https://files.pythonhosted.org/packages/df/c5/d1be898bf0dc3ef9030c3825e5d3b83f2c528d207d246cbabe245966808d/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:2b7d8f6c26245217bd2ad053761201e9f9680f8ce52f0fcd8d0755aeae5b2152", size = 149687, upload-time = "2025-10-14T04:41:46.442Z" }, + { url = "https://files.pythonhosted.org/packages/a5/42/90c1f7b9341eef50c8a1cb3f098ac43b0508413f33affd762855f67a410e/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:799a7a5e4fb2d5898c60b640fd4981d6a25f1c11790935a44ce38c54e985f828", size = 160014, upload-time = "2025-10-14T04:41:47.631Z" }, + { url = "https://files.pythonhosted.org/packages/76/be/4d3ee471e8145d12795ab655ece37baed0929462a86e72372fd25859047c/charset_normalizer-3.4.4-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:99ae2cffebb06e6c22bdc25801d7b30f503cc87dbd283479e7b606f70aff57ec", size = 154044, upload-time = "2025-10-14T04:41:48.81Z" }, + { url = "https://files.pythonhosted.org/packages/b0/6f/8f7af07237c34a1defe7defc565a9bc1807762f672c0fde711a4b22bf9c0/charset_normalizer-3.4.4-cp314-cp314-win32.whl", hash = "sha256:f9d332f8c2a2fcbffe1378594431458ddbef721c1769d78e2cbc06280d8155f9", size = 99940, upload-time = "2025-10-14T04:41:49.946Z" }, + { url = "https://files.pythonhosted.org/packages/4b/51/8ade005e5ca5b0d80fb4aff72a3775b325bdc3d27408c8113811a7cbe640/charset_normalizer-3.4.4-cp314-cp314-win_amd64.whl", hash = "sha256:8a6562c3700cce886c5be75ade4a5db4214fda19fede41d9792d100288d8f94c", size = 107104, upload-time = "2025-10-14T04:41:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/da/5f/6b8f83a55bb8278772c5ae54a577f3099025f9ade59d0136ac24a0df4bde/charset_normalizer-3.4.4-cp314-cp314-win_arm64.whl", hash = "sha256:de00632ca48df9daf77a2c65a484531649261ec9f25489917f09e455cb09ddb2", size = 100743, upload-time = "2025-10-14T04:41:52.122Z" }, + { url = "https://files.pythonhosted.org/packages/46/7c/0c4760bccf082737ca7ab84a4c2034fcc06b1f21cf3032ea98bd6feb1725/charset_normalizer-3.4.4-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a9768c477b9d7bd54bc0c86dbaebdec6f03306675526c9927c0e8a04e8f94af9", size = 209609, upload-time = "2025-10-14T04:42:10.922Z" }, + { url = "https://files.pythonhosted.org/packages/bb/a4/69719daef2f3d7f1819de60c9a6be981b8eeead7542d5ec4440f3c80e111/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1bee1e43c28aa63cb16e5c14e582580546b08e535299b8b6158a7c9c768a1f3d", size = 149029, upload-time = "2025-10-14T04:42:12.38Z" }, + { url = "https://files.pythonhosted.org/packages/e6/21/8d4e1d6c1e6070d3672908b8e4533a71b5b53e71d16828cc24d0efec564c/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:fd44c878ea55ba351104cb93cc85e74916eb8fa440ca7903e57575e97394f608", size = 144580, upload-time = "2025-10-14T04:42:13.549Z" }, + { url = "https://files.pythonhosted.org/packages/a7/0a/a616d001b3f25647a9068e0b9199f697ce507ec898cacb06a0d5a1617c99/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0f04b14ffe5fdc8c4933862d8306109a2c51e0704acfa35d51598eb45a1e89fc", size = 162340, upload-time = "2025-10-14T04:42:14.892Z" }, + { url = "https://files.pythonhosted.org/packages/85/93/060b52deb249a5450460e0585c88a904a83aec474ab8e7aba787f45e79f2/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:cd09d08005f958f370f539f186d10aec3377d55b9eeb0d796025d4886119d76e", size = 159619, upload-time = "2025-10-14T04:42:16.676Z" }, + { url = "https://files.pythonhosted.org/packages/dd/21/0274deb1cc0632cd587a9a0ec6b4674d9108e461cb4cd40d457adaeb0564/charset_normalizer-3.4.4-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4fe7859a4e3e8457458e2ff592f15ccb02f3da787fcd31e0183879c3ad4692a1", size = 153980, upload-time = "2025-10-14T04:42:17.917Z" }, + { url = "https://files.pythonhosted.org/packages/28/2b/e3d7d982858dccc11b31906976323d790dded2017a0572f093ff982d692f/charset_normalizer-3.4.4-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa09f53c465e532f4d3db095e0c55b615f010ad81803d383195b6b5ca6cbf5f3", size = 152174, upload-time = "2025-10-14T04:42:19.018Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ff/4a269f8e35f1e58b2df52c131a1fa019acb7ef3f8697b7d464b07e9b492d/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:7fa17817dc5625de8a027cb8b26d9fefa3ea28c8253929b8d6649e705d2835b6", size = 151666, upload-time = "2025-10-14T04:42:20.171Z" }, + { url = "https://files.pythonhosted.org/packages/da/c9/ec39870f0b330d58486001dd8e532c6b9a905f5765f58a6f8204926b4a93/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5947809c8a2417be3267efc979c47d76a079758166f7d43ef5ae8e9f92751f88", size = 145550, upload-time = "2025-10-14T04:42:21.324Z" }, + { url = "https://files.pythonhosted.org/packages/75/8f/d186ab99e40e0ed9f82f033d6e49001701c81244d01905dd4a6924191a30/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:4902828217069c3c5c71094537a8e623f5d097858ac6ca8252f7b4d10b7560f1", size = 163721, upload-time = "2025-10-14T04:42:22.46Z" }, + { url = "https://files.pythonhosted.org/packages/96/b1/6047663b9744df26a7e479ac1e77af7134b1fcf9026243bb48ee2d18810f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:7c308f7e26e4363d79df40ca5b2be1c6ba9f02bdbccfed5abddb7859a6ce72cf", size = 152127, upload-time = "2025-10-14T04:42:23.712Z" }, + { url = "https://files.pythonhosted.org/packages/59/78/e5a6eac9179f24f704d1be67d08704c3c6ab9f00963963524be27c18ed87/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:2c9d3c380143a1fedbff95a312aa798578371eb29da42106a29019368a475318", size = 161175, upload-time = "2025-10-14T04:42:24.87Z" }, + { url = "https://files.pythonhosted.org/packages/e5/43/0e626e42d54dd2f8dd6fc5e1c5ff00f05fbca17cb699bedead2cae69c62f/charset_normalizer-3.4.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:cb01158d8b88ee68f15949894ccc6712278243d95f344770fa7593fa2d94410c", size = 155375, upload-time = "2025-10-14T04:42:27.246Z" }, + { url = "https://files.pythonhosted.org/packages/e9/91/d9615bf2e06f35e4997616ff31248c3657ed649c5ab9d35ea12fce54e380/charset_normalizer-3.4.4-cp39-cp39-win32.whl", hash = "sha256:2677acec1a2f8ef614c6888b5b4ae4060cc184174a938ed4e8ef690e15d3e505", size = 99692, upload-time = "2025-10-14T04:42:28.425Z" }, + { url = "https://files.pythonhosted.org/packages/d1/a9/6c040053909d9d1ef4fcab45fddec083aedc9052c10078339b47c8573ea8/charset_normalizer-3.4.4-cp39-cp39-win_amd64.whl", hash = "sha256:f8e160feb2aed042cd657a72acc0b481212ed28b1b9a95c0cee1621b524e1966", size = 107192, upload-time = "2025-10-14T04:42:29.482Z" }, + { url = "https://files.pythonhosted.org/packages/f0/c6/4fa536b2c0cd3edfb7ccf8469fa0f363ea67b7213a842b90909ca33dd851/charset_normalizer-3.4.4-cp39-cp39-win_arm64.whl", hash = "sha256:b5d84d37db046c5ca74ee7bb47dd6cbc13f80665fdde3e8040bdd3fb015ecb50", size = 100220, upload-time = "2025-10-14T04:42:30.632Z" }, + { url = "https://files.pythonhosted.org/packages/0a/4c/925909008ed5a988ccbb72dcc897407e5d6d3bd72410d69e051fc0c14647/charset_normalizer-3.4.4-py3-none-any.whl", hash = "sha256:7a32c560861a02ff789ad905a2fe94e3f840803362c84fecf1851cb4cf3dc37f", size = 53402, upload-time = "2025-10-14T04:42:31.76Z" }, +] + +[[package]] +name = "cibuildwheel" +version = "2.23.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", + "python_full_version < '3.10'", +] +dependencies = [ + { name = "bashlex", marker = "python_full_version < '3.11'" }, + { name = "bracex", marker = "python_full_version < '3.11'" }, + { name = "certifi", marker = "python_full_version < '3.11'" }, + { name = "dependency-groups", marker = "python_full_version < '3.11'" }, + { name = "filelock", version = "3.19.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "filelock", version = "3.24.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "packaging", marker = "python_full_version < '3.11'" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "platformdirs", version = "4.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/f5/2c06c8229e291e121cb26ed2efa1ba5d89053a93631d8f1d795f2dacabb8/cibuildwheel-2.23.3.tar.gz", hash = "sha256:d85dd15b7eb81711900d8129e67efb32b12f99cc00fc271ab060fa6270c38397", size = 295383, upload-time = "2025-04-26T10:41:28.258Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/17/8e/127e75e087c0a55903deb447a938e97935c6a56bfd20e6070bcc26c06d1b/cibuildwheel-2.23.3-py3-none-any.whl", hash = "sha256:0fa40073ae23a56d5f995d8405e82c1206049999bb89b92aa0835ee62ab8a891", size = 91792, upload-time = "2025-04-26T10:41:26.148Z" }, +] + +[[package]] +name = "cibuildwheel" +version = "3.3.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "bashlex", marker = "python_full_version >= '3.11'" }, + { name = "bracex", marker = "python_full_version >= '3.11'" }, + { name = "build", marker = "python_full_version >= '3.11'" }, + { name = "certifi", marker = "python_full_version >= '3.11'" }, + { name = "dependency-groups", marker = "python_full_version >= '3.11'" }, + { name = "filelock", version = "3.24.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "humanize", marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.11'" }, + { name = "patchelf", marker = "(python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'darwin') or (python_full_version >= '3.11' and platform_machine == 'aarch64' and sys_platform == 'linux') or (python_full_version >= '3.11' and platform_machine == 'arm64' and sys_platform == 'linux') or (python_full_version >= '3.11' and platform_machine == 'x86_64' and sys_platform == 'linux')" }, + { name = "platformdirs", version = "4.9.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pyelftools", marker = "python_full_version >= '3.11'" }, + { name = "wheel", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/8c/61b0cca8be7eef6af32b0c0e62f78dc8c91572ea8870bc6c4d061f259832/cibuildwheel-3.3.1.tar.gz", hash = "sha256:ae6eafe6f7ed3bab38919e08bf3eb92085f6387c5f7a746b40cc4775a8462f9a", size = 358374, upload-time = "2026-01-05T19:58:17.606Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/73/5c/3b6689ae5c8a720c275f9832c2d1611a2a1f445bfb16701a13f5f604af5c/cibuildwheel-3.3.1-py3-none-any.whl", hash = "sha256:6d3c387e77c5850819294863eeee4e57fb7e8ecdf87b7412e763222c16e26424", size = 127164, upload-time = "2026-01-05T19:58:16.338Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/f6/31a8f28b4a2a4fa0e01085e542f3081ab0588eff8e589d39d775172c9792/contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4", size = 13464370, upload-time = "2024-08-27T21:00:03.328Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6c/e0/be8dcc796cfdd96708933e0e2da99ba4bb8f9b2caa9d560a50f3f09a65f3/contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7", size = 265366, upload-time = "2024-08-27T20:50:09.947Z" }, + { url = "https://files.pythonhosted.org/packages/50/d6/c953b400219443535d412fcbbc42e7a5e823291236bc0bb88936e3cc9317/contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42", size = 249226, upload-time = "2024-08-27T20:50:16.1Z" }, + { url = "https://files.pythonhosted.org/packages/6f/b4/6fffdf213ffccc28483c524b9dad46bb78332851133b36ad354b856ddc7c/contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7", size = 308460, upload-time = "2024-08-27T20:50:22.536Z" }, + { url = "https://files.pythonhosted.org/packages/cf/6c/118fc917b4050f0afe07179a6dcbe4f3f4ec69b94f36c9e128c4af480fb8/contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab", size = 347623, upload-time = "2024-08-27T20:50:28.806Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a4/30ff110a81bfe3abf7b9673284d21ddce8cc1278f6f77393c91199da4c90/contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589", size = 317761, upload-time = "2024-08-27T20:50:35.126Z" }, + { url = "https://files.pythonhosted.org/packages/99/e6/d11966962b1aa515f5586d3907ad019f4b812c04e4546cc19ebf62b5178e/contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41", size = 322015, upload-time = "2024-08-27T20:50:40.318Z" }, + { url = "https://files.pythonhosted.org/packages/4d/e3/182383743751d22b7b59c3c753277b6aee3637049197624f333dac5b4c80/contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d", size = 1262672, upload-time = "2024-08-27T20:50:55.643Z" }, + { url = "https://files.pythonhosted.org/packages/78/53/974400c815b2e605f252c8fb9297e2204347d1755a5374354ee77b1ea259/contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223", size = 1321688, upload-time = "2024-08-27T20:51:11.293Z" }, + { url = "https://files.pythonhosted.org/packages/52/29/99f849faed5593b2926a68a31882af98afbeac39c7fdf7de491d9c85ec6a/contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f", size = 171145, upload-time = "2024-08-27T20:51:15.2Z" }, + { url = "https://files.pythonhosted.org/packages/a9/97/3f89bba79ff6ff2b07a3cbc40aa693c360d5efa90d66e914f0ff03b95ec7/contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b", size = 216019, upload-time = "2024-08-27T20:51:19.365Z" }, + { url = "https://files.pythonhosted.org/packages/b3/1f/9375917786cb39270b0ee6634536c0e22abf225825602688990d8f5c6c19/contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad", size = 266356, upload-time = "2024-08-27T20:51:24.146Z" }, + { url = "https://files.pythonhosted.org/packages/05/46/9256dd162ea52790c127cb58cfc3b9e3413a6e3478917d1f811d420772ec/contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49", size = 250915, upload-time = "2024-08-27T20:51:28.683Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5d/3056c167fa4486900dfbd7e26a2fdc2338dc58eee36d490a0ed3ddda5ded/contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66", size = 310443, upload-time = "2024-08-27T20:51:33.675Z" }, + { url = "https://files.pythonhosted.org/packages/ca/c2/1a612e475492e07f11c8e267ea5ec1ce0d89971be496c195e27afa97e14a/contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081", size = 348548, upload-time = "2024-08-27T20:51:39.322Z" }, + { url = "https://files.pythonhosted.org/packages/45/cf/2c2fc6bb5874158277b4faf136847f0689e1b1a1f640a36d76d52e78907c/contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1", size = 319118, upload-time = "2024-08-27T20:51:44.717Z" }, + { url = "https://files.pythonhosted.org/packages/03/33/003065374f38894cdf1040cef474ad0546368eea7e3a51d48b8a423961f8/contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d", size = 323162, upload-time = "2024-08-27T20:51:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/42/80/e637326e85e4105a802e42959f56cff2cd39a6b5ef68d5d9aee3ea5f0e4c/contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c", size = 1265396, upload-time = "2024-08-27T20:52:04.926Z" }, + { url = "https://files.pythonhosted.org/packages/7c/3b/8cbd6416ca1bbc0202b50f9c13b2e0b922b64be888f9d9ee88e6cfabfb51/contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb", size = 1324297, upload-time = "2024-08-27T20:52:21.843Z" }, + { url = "https://files.pythonhosted.org/packages/4d/2c/021a7afaa52fe891f25535506cc861c30c3c4e5a1c1ce94215e04b293e72/contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c", size = 171808, upload-time = "2024-08-27T20:52:25.163Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/804f02ff30a7fae21f98198828d0857439ec4c91a96e20cf2d6c49372966/contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67", size = 217181, upload-time = "2024-08-27T20:52:29.13Z" }, + { url = "https://files.pythonhosted.org/packages/c9/92/8e0bbfe6b70c0e2d3d81272b58c98ac69ff1a4329f18c73bd64824d8b12e/contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f", size = 267838, upload-time = "2024-08-27T20:52:33.911Z" }, + { url = "https://files.pythonhosted.org/packages/e3/04/33351c5d5108460a8ce6d512307690b023f0cfcad5899499f5c83b9d63b1/contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6", size = 251549, upload-time = "2024-08-27T20:52:39.179Z" }, + { url = "https://files.pythonhosted.org/packages/51/3d/aa0fe6ae67e3ef9f178389e4caaaa68daf2f9024092aa3c6032e3d174670/contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639", size = 303177, upload-time = "2024-08-27T20:52:44.789Z" }, + { url = "https://files.pythonhosted.org/packages/56/c3/c85a7e3e0cab635575d3b657f9535443a6f5d20fac1a1911eaa4bbe1aceb/contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c", size = 341735, upload-time = "2024-08-27T20:52:51.05Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8d/20f7a211a7be966a53f474bc90b1a8202e9844b3f1ef85f3ae45a77151ee/contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06", size = 314679, upload-time = "2024-08-27T20:52:58.473Z" }, + { url = "https://files.pythonhosted.org/packages/6e/be/524e377567defac0e21a46e2a529652d165fed130a0d8a863219303cee18/contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09", size = 320549, upload-time = "2024-08-27T20:53:06.593Z" }, + { url = "https://files.pythonhosted.org/packages/0f/96/fdb2552a172942d888915f3a6663812e9bc3d359d53dafd4289a0fb462f0/contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd", size = 1263068, upload-time = "2024-08-27T20:53:23.442Z" }, + { url = "https://files.pythonhosted.org/packages/2a/25/632eab595e3140adfa92f1322bf8915f68c932bac468e89eae9974cf1c00/contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35", size = 1322833, upload-time = "2024-08-27T20:53:39.243Z" }, + { url = "https://files.pythonhosted.org/packages/73/e3/69738782e315a1d26d29d71a550dbbe3eb6c653b028b150f70c1a5f4f229/contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb", size = 172681, upload-time = "2024-08-27T20:53:43.05Z" }, + { url = "https://files.pythonhosted.org/packages/0c/89/9830ba00d88e43d15e53d64931e66b8792b46eb25e2050a88fec4a0df3d5/contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b", size = 218283, upload-time = "2024-08-27T20:53:47.232Z" }, + { url = "https://files.pythonhosted.org/packages/53/a1/d20415febfb2267af2d7f06338e82171824d08614084714fb2c1dac9901f/contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3", size = 267879, upload-time = "2024-08-27T20:53:51.597Z" }, + { url = "https://files.pythonhosted.org/packages/aa/45/5a28a3570ff6218d8bdfc291a272a20d2648104815f01f0177d103d985e1/contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7", size = 251573, upload-time = "2024-08-27T20:53:55.659Z" }, + { url = "https://files.pythonhosted.org/packages/39/1c/d3f51540108e3affa84f095c8b04f0aa833bb797bc8baa218a952a98117d/contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84", size = 303184, upload-time = "2024-08-27T20:54:00.225Z" }, + { url = "https://files.pythonhosted.org/packages/00/56/1348a44fb6c3a558c1a3a0cd23d329d604c99d81bf5a4b58c6b71aab328f/contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0", size = 340262, upload-time = "2024-08-27T20:54:05.234Z" }, + { url = "https://files.pythonhosted.org/packages/2b/23/00d665ba67e1bb666152131da07e0f24c95c3632d7722caa97fb61470eca/contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b", size = 313806, upload-time = "2024-08-27T20:54:09.889Z" }, + { url = "https://files.pythonhosted.org/packages/5a/42/3cf40f7040bb8362aea19af9a5fb7b32ce420f645dd1590edcee2c657cd5/contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da", size = 319710, upload-time = "2024-08-27T20:54:14.536Z" }, + { url = "https://files.pythonhosted.org/packages/05/32/f3bfa3fc083b25e1a7ae09197f897476ee68e7386e10404bdf9aac7391f0/contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14", size = 1264107, upload-time = "2024-08-27T20:54:29.735Z" }, + { url = "https://files.pythonhosted.org/packages/1c/1e/1019d34473a736664f2439542b890b2dc4c6245f5c0d8cdfc0ccc2cab80c/contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8", size = 1322458, upload-time = "2024-08-27T20:54:45.507Z" }, + { url = "https://files.pythonhosted.org/packages/22/85/4f8bfd83972cf8909a4d36d16b177f7b8bdd942178ea4bf877d4a380a91c/contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294", size = 172643, upload-time = "2024-08-27T20:55:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/cc/4a/fb3c83c1baba64ba90443626c228ca14f19a87c51975d3b1de308dd2cf08/contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087", size = 218301, upload-time = "2024-08-27T20:55:56.509Z" }, + { url = "https://files.pythonhosted.org/packages/76/65/702f4064f397821fea0cb493f7d3bc95a5d703e20954dce7d6d39bacf378/contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8", size = 278972, upload-time = "2024-08-27T20:54:50.347Z" }, + { url = "https://files.pythonhosted.org/packages/80/85/21f5bba56dba75c10a45ec00ad3b8190dbac7fd9a8a8c46c6116c933e9cf/contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b", size = 263375, upload-time = "2024-08-27T20:54:54.909Z" }, + { url = "https://files.pythonhosted.org/packages/0a/64/084c86ab71d43149f91ab3a4054ccf18565f0a8af36abfa92b1467813ed6/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973", size = 307188, upload-time = "2024-08-27T20:55:00.184Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ff/d61a4c288dc42da0084b8d9dc2aa219a850767165d7d9a9c364ff530b509/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18", size = 345644, upload-time = "2024-08-27T20:55:05.673Z" }, + { url = "https://files.pythonhosted.org/packages/ca/aa/00d2313d35ec03f188e8f0786c2fc61f589306e02fdc158233697546fd58/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8", size = 317141, upload-time = "2024-08-27T20:55:11.047Z" }, + { url = "https://files.pythonhosted.org/packages/8d/6a/b5242c8cb32d87f6abf4f5e3044ca397cb1a76712e3fa2424772e3ff495f/contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6", size = 323469, upload-time = "2024-08-27T20:55:15.914Z" }, + { url = "https://files.pythonhosted.org/packages/6f/a6/73e929d43028a9079aca4bde107494864d54f0d72d9db508a51ff0878593/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2", size = 1260894, upload-time = "2024-08-27T20:55:31.553Z" }, + { url = "https://files.pythonhosted.org/packages/2b/1e/1e726ba66eddf21c940821df8cf1a7d15cb165f0682d62161eaa5e93dae1/contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927", size = 1314829, upload-time = "2024-08-27T20:55:47.837Z" }, + { url = "https://files.pythonhosted.org/packages/b3/e3/b9f72758adb6ef7397327ceb8b9c39c75711affb220e4f53c745ea1d5a9a/contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8", size = 265518, upload-time = "2024-08-27T20:56:01.333Z" }, + { url = "https://files.pythonhosted.org/packages/ec/22/19f5b948367ab5260fb41d842c7a78dae645603881ea6bc39738bcfcabf6/contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c", size = 249350, upload-time = "2024-08-27T20:56:05.432Z" }, + { url = "https://files.pythonhosted.org/packages/26/76/0c7d43263dd00ae21a91a24381b7e813d286a3294d95d179ef3a7b9fb1d7/contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca", size = 309167, upload-time = "2024-08-27T20:56:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/96/3b/cadff6773e89f2a5a492c1a8068e21d3fccaf1a1c1df7d65e7c8e3ef60ba/contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f", size = 348279, upload-time = "2024-08-27T20:56:15.41Z" }, + { url = "https://files.pythonhosted.org/packages/e1/86/158cc43aa549d2081a955ab11c6bdccc7a22caacc2af93186d26f5f48746/contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc", size = 318519, upload-time = "2024-08-27T20:56:21.813Z" }, + { url = "https://files.pythonhosted.org/packages/05/11/57335544a3027e9b96a05948c32e566328e3a2f84b7b99a325b7a06d2b06/contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2", size = 321922, upload-time = "2024-08-27T20:56:26.983Z" }, + { url = "https://files.pythonhosted.org/packages/0b/e3/02114f96543f4a1b694333b92a6dcd4f8eebbefcc3a5f3bbb1316634178f/contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e", size = 1258017, upload-time = "2024-08-27T20:56:42.246Z" }, + { url = "https://files.pythonhosted.org/packages/f3/3b/bfe4c81c6d5881c1c643dde6620be0b42bf8aab155976dd644595cfab95c/contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800", size = 1316773, upload-time = "2024-08-27T20:56:58.58Z" }, + { url = "https://files.pythonhosted.org/packages/f1/17/c52d2970784383cafb0bd918b6fb036d98d96bbf0bc1befb5d1e31a07a70/contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5", size = 171353, upload-time = "2024-08-27T20:57:02.718Z" }, + { url = "https://files.pythonhosted.org/packages/53/23/db9f69676308e094d3c45f20cc52e12d10d64f027541c995d89c11ad5c75/contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843", size = 211817, upload-time = "2024-08-27T20:57:06.328Z" }, + { url = "https://files.pythonhosted.org/packages/d1/09/60e486dc2b64c94ed33e58dcfb6f808192c03dfc5574c016218b9b7680dc/contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c", size = 261886, upload-time = "2024-08-27T20:57:10.863Z" }, + { url = "https://files.pythonhosted.org/packages/19/20/b57f9f7174fcd439a7789fb47d764974ab646fa34d1790551de386457a8e/contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779", size = 311008, upload-time = "2024-08-27T20:57:15.588Z" }, + { url = "https://files.pythonhosted.org/packages/74/fc/5040d42623a1845d4f17a418e590fd7a79ae8cb2bad2b2f83de63c3bdca4/contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4", size = 215690, upload-time = "2024-08-27T20:57:19.321Z" }, + { url = "https://files.pythonhosted.org/packages/2b/24/dc3dcd77ac7460ab7e9d2b01a618cb31406902e50e605a8d6091f0a8f7cc/contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0", size = 261894, upload-time = "2024-08-27T20:57:23.873Z" }, + { url = "https://files.pythonhosted.org/packages/b1/db/531642a01cfec39d1682e46b5457b07cf805e3c3c584ec27e2a6223f8f6c/contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102", size = 311099, upload-time = "2024-08-27T20:57:28.58Z" }, + { url = "https://files.pythonhosted.org/packages/38/1e/94bda024d629f254143a134eead69e21c836429a2a6ce82209a00ddcb79a/contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb", size = 215838, upload-time = "2024-08-27T20:57:32.913Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/54/eb9bfc647b19f2009dd5c7f5ec51c4e6ca831725f1aea7a993034f483147/contourpy-1.3.2.tar.gz", hash = "sha256:b6945942715a034c671b7fc54f9588126b0b8bf23db2696e3ca8328f3ff0ab54", size = 13466130, upload-time = "2025-04-15T17:47:53.79Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/12/a3/da4153ec8fe25d263aa48c1a4cbde7f49b59af86f0b6f7862788c60da737/contourpy-1.3.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ba38e3f9f330af820c4b27ceb4b9c7feee5fe0493ea53a8720f4792667465934", size = 268551, upload-time = "2025-04-15T17:34:46.581Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6c/330de89ae1087eb622bfca0177d32a7ece50c3ef07b28002de4757d9d875/contourpy-1.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dc41ba0714aa2968d1f8674ec97504a8f7e334f48eeacebcaa6256213acb0989", size = 253399, upload-time = "2025-04-15T17:34:51.427Z" }, + { url = "https://files.pythonhosted.org/packages/c1/bd/20c6726b1b7f81a8bee5271bed5c165f0a8e1f572578a9d27e2ccb763cb2/contourpy-1.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9be002b31c558d1ddf1b9b415b162c603405414bacd6932d031c5b5a8b757f0d", size = 312061, upload-time = "2025-04-15T17:34:55.961Z" }, + { url = "https://files.pythonhosted.org/packages/22/fc/a9665c88f8a2473f823cf1ec601de9e5375050f1958cbb356cdf06ef1ab6/contourpy-1.3.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8d2e74acbcba3bfdb6d9d8384cdc4f9260cae86ed9beee8bd5f54fee49a430b9", size = 351956, upload-time = "2025-04-15T17:35:00.992Z" }, + { url = "https://files.pythonhosted.org/packages/25/eb/9f0a0238f305ad8fb7ef42481020d6e20cf15e46be99a1fcf939546a177e/contourpy-1.3.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e259bced5549ac64410162adc973c5e2fb77f04df4a439d00b478e57a0e65512", size = 320872, upload-time = "2025-04-15T17:35:06.177Z" }, + { url = "https://files.pythonhosted.org/packages/32/5c/1ee32d1c7956923202f00cf8d2a14a62ed7517bdc0ee1e55301227fc273c/contourpy-1.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ad687a04bc802cbe8b9c399c07162a3c35e227e2daccf1668eb1f278cb698631", size = 325027, upload-time = "2025-04-15T17:35:11.244Z" }, + { url = "https://files.pythonhosted.org/packages/83/bf/9baed89785ba743ef329c2b07fd0611d12bfecbedbdd3eeecf929d8d3b52/contourpy-1.3.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cdd22595308f53ef2f891040ab2b93d79192513ffccbd7fe19be7aa773a5e09f", size = 1306641, upload-time = "2025-04-15T17:35:26.701Z" }, + { url = "https://files.pythonhosted.org/packages/d4/cc/74e5e83d1e35de2d28bd97033426b450bc4fd96e092a1f7a63dc7369b55d/contourpy-1.3.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b4f54d6a2defe9f257327b0f243612dd051cc43825587520b1bf74a31e2f6ef2", size = 1374075, upload-time = "2025-04-15T17:35:43.204Z" }, + { url = "https://files.pythonhosted.org/packages/0c/42/17f3b798fd5e033b46a16f8d9fcb39f1aba051307f5ebf441bad1ecf78f8/contourpy-1.3.2-cp310-cp310-win32.whl", hash = "sha256:f939a054192ddc596e031e50bb13b657ce318cf13d264f095ce9db7dc6ae81c0", size = 177534, upload-time = "2025-04-15T17:35:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/54/ec/5162b8582f2c994721018d0c9ece9dc6ff769d298a8ac6b6a652c307e7df/contourpy-1.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:c440093bbc8fc21c637c03bafcbef95ccd963bc6e0514ad887932c18ca2a759a", size = 221188, upload-time = "2025-04-15T17:35:50.064Z" }, + { url = "https://files.pythonhosted.org/packages/b3/b9/ede788a0b56fc5b071639d06c33cb893f68b1178938f3425debebe2dab78/contourpy-1.3.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6a37a2fb93d4df3fc4c0e363ea4d16f83195fc09c891bc8ce072b9d084853445", size = 269636, upload-time = "2025-04-15T17:35:54.473Z" }, + { url = "https://files.pythonhosted.org/packages/e6/75/3469f011d64b8bbfa04f709bfc23e1dd71be54d05b1b083be9f5b22750d1/contourpy-1.3.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b7cd50c38f500bbcc9b6a46643a40e0913673f869315d8e70de0438817cb7773", size = 254636, upload-time = "2025-04-15T17:35:58.283Z" }, + { url = "https://files.pythonhosted.org/packages/8d/2f/95adb8dae08ce0ebca4fd8e7ad653159565d9739128b2d5977806656fcd2/contourpy-1.3.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d6658ccc7251a4433eebd89ed2672c2ed96fba367fd25ca9512aa92a4b46c4f1", size = 313053, upload-time = "2025-04-15T17:36:03.235Z" }, + { url = "https://files.pythonhosted.org/packages/c3/a6/8ccf97a50f31adfa36917707fe39c9a0cbc24b3bbb58185577f119736cc9/contourpy-1.3.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:70771a461aaeb335df14deb6c97439973d253ae70660ca085eec25241137ef43", size = 352985, upload-time = "2025-04-15T17:36:08.275Z" }, + { url = "https://files.pythonhosted.org/packages/1d/b6/7925ab9b77386143f39d9c3243fdd101621b4532eb126743201160ffa7e6/contourpy-1.3.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:65a887a6e8c4cd0897507d814b14c54a8c2e2aa4ac9f7686292f9769fcf9a6ab", size = 323750, upload-time = "2025-04-15T17:36:13.29Z" }, + { url = "https://files.pythonhosted.org/packages/c2/f3/20c5d1ef4f4748e52d60771b8560cf00b69d5c6368b5c2e9311bcfa2a08b/contourpy-1.3.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3859783aefa2b8355697f16642695a5b9792e7a46ab86da1118a4a23a51a33d7", size = 326246, upload-time = "2025-04-15T17:36:18.329Z" }, + { url = "https://files.pythonhosted.org/packages/8c/e5/9dae809e7e0b2d9d70c52b3d24cba134dd3dad979eb3e5e71f5df22ed1f5/contourpy-1.3.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:eab0f6db315fa4d70f1d8ab514e527f0366ec021ff853d7ed6a2d33605cf4b83", size = 1308728, upload-time = "2025-04-15T17:36:33.878Z" }, + { url = "https://files.pythonhosted.org/packages/e2/4a/0058ba34aeea35c0b442ae61a4f4d4ca84d6df8f91309bc2d43bb8dd248f/contourpy-1.3.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:d91a3ccc7fea94ca0acab82ceb77f396d50a1f67412efe4c526f5d20264e6ecd", size = 1375762, upload-time = "2025-04-15T17:36:51.295Z" }, + { url = "https://files.pythonhosted.org/packages/09/33/7174bdfc8b7767ef2c08ed81244762d93d5c579336fc0b51ca57b33d1b80/contourpy-1.3.2-cp311-cp311-win32.whl", hash = "sha256:1c48188778d4d2f3d48e4643fb15d8608b1d01e4b4d6b0548d9b336c28fc9b6f", size = 178196, upload-time = "2025-04-15T17:36:55.002Z" }, + { url = "https://files.pythonhosted.org/packages/5e/fe/4029038b4e1c4485cef18e480b0e2cd2d755448bb071eb9977caac80b77b/contourpy-1.3.2-cp311-cp311-win_amd64.whl", hash = "sha256:5ebac872ba09cb8f2131c46b8739a7ff71de28a24c869bcad554477eb089a878", size = 222017, upload-time = "2025-04-15T17:36:58.576Z" }, + { url = "https://files.pythonhosted.org/packages/34/f7/44785876384eff370c251d58fd65f6ad7f39adce4a093c934d4a67a7c6b6/contourpy-1.3.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:4caf2bcd2969402bf77edc4cb6034c7dd7c0803213b3523f111eb7460a51b8d2", size = 271580, upload-time = "2025-04-15T17:37:03.105Z" }, + { url = "https://files.pythonhosted.org/packages/93/3b/0004767622a9826ea3d95f0e9d98cd8729015768075d61f9fea8eeca42a8/contourpy-1.3.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:82199cb78276249796419fe36b7386bd8d2cc3f28b3bc19fe2454fe2e26c4c15", size = 255530, upload-time = "2025-04-15T17:37:07.026Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7bd49e1f4fa805772d9fd130e0d375554ebc771ed7172f48dfcd4ca61549/contourpy-1.3.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:106fab697af11456fcba3e352ad50effe493a90f893fca6c2ca5c033820cea92", size = 307688, upload-time = "2025-04-15T17:37:11.481Z" }, + { url = "https://files.pythonhosted.org/packages/fc/97/e1d5dbbfa170725ef78357a9a0edc996b09ae4af170927ba8ce977e60a5f/contourpy-1.3.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d14f12932a8d620e307f715857107b1d1845cc44fdb5da2bc8e850f5ceba9f87", size = 347331, upload-time = "2025-04-15T17:37:18.212Z" }, + { url = "https://files.pythonhosted.org/packages/6f/66/e69e6e904f5ecf6901be3dd16e7e54d41b6ec6ae3405a535286d4418ffb4/contourpy-1.3.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:532fd26e715560721bb0d5fc7610fce279b3699b018600ab999d1be895b09415", size = 318963, upload-time = "2025-04-15T17:37:22.76Z" }, + { url = "https://files.pythonhosted.org/packages/a8/32/b8a1c8965e4f72482ff2d1ac2cd670ce0b542f203c8e1d34e7c3e6925da7/contourpy-1.3.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b383144cf2d2c29f01a1e8170f50dacf0eac02d64139dcd709a8ac4eb3cfe", size = 323681, upload-time = "2025-04-15T17:37:33.001Z" }, + { url = "https://files.pythonhosted.org/packages/30/c6/12a7e6811d08757c7162a541ca4c5c6a34c0f4e98ef2b338791093518e40/contourpy-1.3.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c49f73e61f1f774650a55d221803b101d966ca0c5a2d6d5e4320ec3997489441", size = 1308674, upload-time = "2025-04-15T17:37:48.64Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8a/bebe5a3f68b484d3a2b8ffaf84704b3e343ef1addea528132ef148e22b3b/contourpy-1.3.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:3d80b2c0300583228ac98d0a927a1ba6a2ba6b8a742463c564f1d419ee5b211e", size = 1380480, upload-time = "2025-04-15T17:38:06.7Z" }, + { url = "https://files.pythonhosted.org/packages/34/db/fcd325f19b5978fb509a7d55e06d99f5f856294c1991097534360b307cf1/contourpy-1.3.2-cp312-cp312-win32.whl", hash = "sha256:90df94c89a91b7362e1142cbee7568f86514412ab8a2c0d0fca72d7e91b62912", size = 178489, upload-time = "2025-04-15T17:38:10.338Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/fadd0b92ffa7b5eb5949bf340a63a4a496a6930a6c37a7ba0f12acb076d6/contourpy-1.3.2-cp312-cp312-win_amd64.whl", hash = "sha256:8c942a01d9163e2e5cfb05cb66110121b8d07ad438a17f9e766317bcb62abf73", size = 223042, upload-time = "2025-04-15T17:38:14.239Z" }, + { url = "https://files.pythonhosted.org/packages/2e/61/5673f7e364b31e4e7ef6f61a4b5121c5f170f941895912f773d95270f3a2/contourpy-1.3.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:de39db2604ae755316cb5967728f4bea92685884b1e767b7c24e983ef5f771cb", size = 271630, upload-time = "2025-04-15T17:38:19.142Z" }, + { url = "https://files.pythonhosted.org/packages/ff/66/a40badddd1223822c95798c55292844b7e871e50f6bfd9f158cb25e0bd39/contourpy-1.3.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f9e896f447c5c8618f1edb2bafa9a4030f22a575ec418ad70611450720b5b08", size = 255670, upload-time = "2025-04-15T17:38:23.688Z" }, + { url = "https://files.pythonhosted.org/packages/1e/c7/cf9fdee8200805c9bc3b148f49cb9482a4e3ea2719e772602a425c9b09f8/contourpy-1.3.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:71e2bd4a1c4188f5c2b8d274da78faab884b59df20df63c34f74aa1813c4427c", size = 306694, upload-time = "2025-04-15T17:38:28.238Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e7/ccb9bec80e1ba121efbffad7f38021021cda5be87532ec16fd96533bb2e0/contourpy-1.3.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de425af81b6cea33101ae95ece1f696af39446db9682a0b56daaa48cfc29f38f", size = 345986, upload-time = "2025-04-15T17:38:33.502Z" }, + { url = "https://files.pythonhosted.org/packages/dc/49/ca13bb2da90391fa4219fdb23b078d6065ada886658ac7818e5441448b78/contourpy-1.3.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:977e98a0e0480d3fe292246417239d2d45435904afd6d7332d8455981c408b85", size = 318060, upload-time = "2025-04-15T17:38:38.672Z" }, + { url = "https://files.pythonhosted.org/packages/c8/65/5245ce8c548a8422236c13ffcdcdada6a2a812c361e9e0c70548bb40b661/contourpy-1.3.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:434f0adf84911c924519d2b08fc10491dd282b20bdd3fa8f60fd816ea0b48841", size = 322747, upload-time = "2025-04-15T17:38:43.712Z" }, + { url = "https://files.pythonhosted.org/packages/72/30/669b8eb48e0a01c660ead3752a25b44fdb2e5ebc13a55782f639170772f9/contourpy-1.3.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c66c4906cdbc50e9cba65978823e6e00b45682eb09adbb78c9775b74eb222422", size = 1308895, upload-time = "2025-04-15T17:39:00.224Z" }, + { url = "https://files.pythonhosted.org/packages/05/5a/b569f4250decee6e8d54498be7bdf29021a4c256e77fe8138c8319ef8eb3/contourpy-1.3.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:8b7fc0cd78ba2f4695fd0a6ad81a19e7e3ab825c31b577f384aa9d7817dc3bef", size = 1379098, upload-time = "2025-04-15T17:43:29.649Z" }, + { url = "https://files.pythonhosted.org/packages/19/ba/b227c3886d120e60e41b28740ac3617b2f2b971b9f601c835661194579f1/contourpy-1.3.2-cp313-cp313-win32.whl", hash = "sha256:15ce6ab60957ca74cff444fe66d9045c1fd3e92c8936894ebd1f3eef2fff075f", size = 178535, upload-time = "2025-04-15T17:44:44.532Z" }, + { url = "https://files.pythonhosted.org/packages/12/6e/2fed56cd47ca739b43e892707ae9a13790a486a3173be063681ca67d2262/contourpy-1.3.2-cp313-cp313-win_amd64.whl", hash = "sha256:e1578f7eafce927b168752ed7e22646dad6cd9bca673c60bff55889fa236ebf9", size = 223096, upload-time = "2025-04-15T17:44:48.194Z" }, + { url = "https://files.pythonhosted.org/packages/54/4c/e76fe2a03014a7c767d79ea35c86a747e9325537a8b7627e0e5b3ba266b4/contourpy-1.3.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0475b1f6604896bc7c53bb070e355e9321e1bc0d381735421a2d2068ec56531f", size = 285090, upload-time = "2025-04-15T17:43:34.084Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e2/5aba47debd55d668e00baf9651b721e7733975dc9fc27264a62b0dd26eb8/contourpy-1.3.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c85bb486e9be652314bb5b9e2e3b0d1b2e643d5eec4992c0fbe8ac71775da739", size = 268643, upload-time = "2025-04-15T17:43:38.626Z" }, + { url = "https://files.pythonhosted.org/packages/a1/37/cd45f1f051fe6230f751cc5cdd2728bb3a203f5619510ef11e732109593c/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:745b57db7758f3ffc05a10254edd3182a2a83402a89c00957a8e8a22f5582823", size = 310443, upload-time = "2025-04-15T17:43:44.522Z" }, + { url = "https://files.pythonhosted.org/packages/8b/a2/36ea6140c306c9ff6dd38e3bcec80b3b018474ef4d17eb68ceecd26675f4/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:970e9173dbd7eba9b4e01aab19215a48ee5dd3f43cef736eebde064a171f89a5", size = 349865, upload-time = "2025-04-15T17:43:49.545Z" }, + { url = "https://files.pythonhosted.org/packages/95/b7/2fc76bc539693180488f7b6cc518da7acbbb9e3b931fd9280504128bf956/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c6c4639a9c22230276b7bffb6a850dfc8258a2521305e1faefe804d006b2e532", size = 321162, upload-time = "2025-04-15T17:43:54.203Z" }, + { url = "https://files.pythonhosted.org/packages/f4/10/76d4f778458b0aa83f96e59d65ece72a060bacb20cfbee46cf6cd5ceba41/contourpy-1.3.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cc829960f34ba36aad4302e78eabf3ef16a3a100863f0d4eeddf30e8a485a03b", size = 327355, upload-time = "2025-04-15T17:44:01.025Z" }, + { url = "https://files.pythonhosted.org/packages/43/a3/10cf483ea683f9f8ab096c24bad3cce20e0d1dd9a4baa0e2093c1c962d9d/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:d32530b534e986374fc19eaa77fcb87e8a99e5431499949b828312bdcd20ac52", size = 1307935, upload-time = "2025-04-15T17:44:17.322Z" }, + { url = "https://files.pythonhosted.org/packages/78/73/69dd9a024444489e22d86108e7b913f3528f56cfc312b5c5727a44188471/contourpy-1.3.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:e298e7e70cf4eb179cc1077be1c725b5fd131ebc81181bf0c03525c8abc297fd", size = 1372168, upload-time = "2025-04-15T17:44:33.43Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1b/96d586ccf1b1a9d2004dd519b25fbf104a11589abfd05484ff12199cca21/contourpy-1.3.2-cp313-cp313t-win32.whl", hash = "sha256:d0e589ae0d55204991450bb5c23f571c64fe43adaa53f93fc902a84c96f52fe1", size = 189550, upload-time = "2025-04-15T17:44:37.092Z" }, + { url = "https://files.pythonhosted.org/packages/b0/e6/6000d0094e8a5e32ad62591c8609e269febb6e4db83a1c75ff8868b42731/contourpy-1.3.2-cp313-cp313t-win_amd64.whl", hash = "sha256:78e9253c3de756b3f6a5174d024c4835acd59eb3f8e2ca13e775dbffe1558f69", size = 238214, upload-time = "2025-04-15T17:44:40.827Z" }, + { url = "https://files.pythonhosted.org/packages/33/05/b26e3c6ecc05f349ee0013f0bb850a761016d89cec528a98193a48c34033/contourpy-1.3.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fd93cc7f3139b6dd7aab2f26a90dde0aa9fc264dbf70f6740d498a70b860b82c", size = 265681, upload-time = "2025-04-15T17:44:59.314Z" }, + { url = "https://files.pythonhosted.org/packages/2b/25/ac07d6ad12affa7d1ffed11b77417d0a6308170f44ff20fa1d5aa6333f03/contourpy-1.3.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:107ba8a6a7eec58bb475329e6d3b95deba9440667c4d62b9b6063942b61d7f16", size = 315101, upload-time = "2025-04-15T17:45:04.165Z" }, + { url = "https://files.pythonhosted.org/packages/8f/4d/5bb3192bbe9d3f27e3061a6a8e7733c9120e203cb8515767d30973f71030/contourpy-1.3.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:ded1706ed0c1049224531b81128efbd5084598f18d8a2d9efae833edbd2b40ad", size = 220599, upload-time = "2025-04-15T17:45:08.456Z" }, + { url = "https://files.pythonhosted.org/packages/ff/c0/91f1215d0d9f9f343e4773ba6c9b89e8c0cc7a64a6263f21139da639d848/contourpy-1.3.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:5f5964cdad279256c084b69c3f412b7801e15356b16efa9d78aa974041903da0", size = 266807, upload-time = "2025-04-15T17:45:15.535Z" }, + { url = "https://files.pythonhosted.org/packages/d4/79/6be7e90c955c0487e7712660d6cead01fa17bff98e0ea275737cc2bc8e71/contourpy-1.3.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b65a95d642d4efa8f64ba12558fcb83407e58a2dfba9d796d77b63ccfcaff5", size = 318729, upload-time = "2025-04-15T17:45:20.166Z" }, + { url = "https://files.pythonhosted.org/packages/87/68/7f46fb537958e87427d98a4074bcde4b67a70b04900cfc5ce29bc2f556c1/contourpy-1.3.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:8c5acb8dddb0752bf252e01a3035b21443158910ac16a3b0d20e7fed7d534ce5", size = 221791, upload-time = "2025-04-15T17:45:24.794Z" }, +] + +[[package]] +name = "contourpy" +version = "1.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" }, + { url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" }, + { url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" }, + { url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" }, + { url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" }, + { url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" }, + { url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" }, + { url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" }, + { url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" }, + { url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" }, + { url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" }, + { url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" }, + { url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" }, + { url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" }, + { url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" }, + { url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" }, + { url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" }, + { url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" }, + { url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" }, + { url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" }, + { url = "https://files.pythonhosted.org/packages/68/35/0167aad910bbdb9599272bd96d01a9ec6852f36b9455cf2ca67bd4cc2d23/contourpy-1.3.3-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:177fb367556747a686509d6fef71d221a4b198a3905fe824430e5ea0fda54eb5", size = 293257, upload-time = "2025-07-26T12:01:39.367Z" }, + { url = "https://files.pythonhosted.org/packages/96/e4/7adcd9c8362745b2210728f209bfbcf7d91ba868a2c5f40d8b58f54c509b/contourpy-1.3.3-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:d002b6f00d73d69333dac9d0b8d5e84d9724ff9ef044fd63c5986e62b7c9e1b1", size = 274034, upload-time = "2025-07-26T12:01:40.645Z" }, + { url = "https://files.pythonhosted.org/packages/73/23/90e31ceeed1de63058a02cb04b12f2de4b40e3bef5e082a7c18d9c8ae281/contourpy-1.3.3-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:348ac1f5d4f1d66d3322420f01d42e43122f43616e0f194fc1c9f5d830c5b286", size = 334672, upload-time = "2025-07-26T12:01:41.942Z" }, + { url = "https://files.pythonhosted.org/packages/ed/93/b43d8acbe67392e659e1d984700e79eb67e2acb2bd7f62012b583a7f1b55/contourpy-1.3.3-cp313-cp313-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:655456777ff65c2c548b7c454af9c6f33f16c8884f11083244b5819cc214f1b5", size = 381234, upload-time = "2025-07-26T12:01:43.499Z" }, + { url = "https://files.pythonhosted.org/packages/46/3b/bec82a3ea06f66711520f75a40c8fc0b113b2a75edb36aa633eb11c4f50f/contourpy-1.3.3-cp313-cp313-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:644a6853d15b2512d67881586bd03f462c7ab755db95f16f14d7e238f2852c67", size = 385169, upload-time = "2025-07-26T12:01:45.219Z" }, + { url = "https://files.pythonhosted.org/packages/4b/32/e0f13a1c5b0f8572d0ec6ae2f6c677b7991fafd95da523159c19eff0696a/contourpy-1.3.3-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4debd64f124ca62069f313a9cb86656ff087786016d76927ae2cf37846b006c9", size = 362859, upload-time = "2025-07-26T12:01:46.519Z" }, + { url = "https://files.pythonhosted.org/packages/33/71/e2a7945b7de4e58af42d708a219f3b2f4cff7386e6b6ab0a0fa0033c49a9/contourpy-1.3.3-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a15459b0f4615b00bbd1e91f1b9e19b7e63aea7483d03d804186f278c0af2659", size = 1332062, upload-time = "2025-07-26T12:01:48.964Z" }, + { url = "https://files.pythonhosted.org/packages/12/fc/4e87ac754220ccc0e807284f88e943d6d43b43843614f0a8afa469801db0/contourpy-1.3.3-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:ca0fdcd73925568ca027e0b17ab07aad764be4706d0a925b89227e447d9737b7", size = 1403932, upload-time = "2025-07-26T12:01:51.979Z" }, + { url = "https://files.pythonhosted.org/packages/a6/2e/adc197a37443f934594112222ac1aa7dc9a98faf9c3842884df9a9d8751d/contourpy-1.3.3-cp313-cp313-win32.whl", hash = "sha256:b20c7c9a3bf701366556e1b1984ed2d0cedf999903c51311417cf5f591d8c78d", size = 185024, upload-time = "2025-07-26T12:01:53.245Z" }, + { url = "https://files.pythonhosted.org/packages/18/0b/0098c214843213759692cc638fce7de5c289200a830e5035d1791d7a2338/contourpy-1.3.3-cp313-cp313-win_amd64.whl", hash = "sha256:1cadd8b8969f060ba45ed7c1b714fe69185812ab43bd6b86a9123fe8f99c3263", size = 226578, upload-time = "2025-07-26T12:01:54.422Z" }, + { url = "https://files.pythonhosted.org/packages/8a/9a/2f6024a0c5995243cd63afdeb3651c984f0d2bc727fd98066d40e141ad73/contourpy-1.3.3-cp313-cp313-win_arm64.whl", hash = "sha256:fd914713266421b7536de2bfa8181aa8c699432b6763a0ea64195ebe28bff6a9", size = 193524, upload-time = "2025-07-26T12:01:55.73Z" }, + { url = "https://files.pythonhosted.org/packages/c0/b3/f8a1a86bd3298513f500e5b1f5fd92b69896449f6cab6a146a5d52715479/contourpy-1.3.3-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:88df9880d507169449d434c293467418b9f6cbe82edd19284aa0409e7fdb933d", size = 306730, upload-time = "2025-07-26T12:01:57.051Z" }, + { url = "https://files.pythonhosted.org/packages/3f/11/4780db94ae62fc0c2053909b65dc3246bd7cecfc4f8a20d957ad43aa4ad8/contourpy-1.3.3-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:d06bb1f751ba5d417047db62bca3c8fde202b8c11fb50742ab3ab962c81e8216", size = 287897, upload-time = "2025-07-26T12:01:58.663Z" }, + { url = "https://files.pythonhosted.org/packages/ae/15/e59f5f3ffdd6f3d4daa3e47114c53daabcb18574a26c21f03dc9e4e42ff0/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e4e6b05a45525357e382909a4c1600444e2a45b4795163d3b22669285591c1ae", size = 326751, upload-time = "2025-07-26T12:02:00.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/81/03b45cfad088e4770b1dcf72ea78d3802d04200009fb364d18a493857210/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ab3074b48c4e2cf1a960e6bbeb7f04566bf36b1861d5c9d4d8ac04b82e38ba20", size = 375486, upload-time = "2025-07-26T12:02:02.128Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ba/49923366492ffbdd4486e970d421b289a670ae8cf539c1ea9a09822b371a/contourpy-1.3.3-cp313-cp313t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c3d53c796f8647d6deb1abe867daeb66dcc8a97e8455efa729516b997b8ed99", size = 388106, upload-time = "2025-07-26T12:02:03.615Z" }, + { url = "https://files.pythonhosted.org/packages/9f/52/5b00ea89525f8f143651f9f03a0df371d3cbd2fccd21ca9b768c7a6500c2/contourpy-1.3.3-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50ed930df7289ff2a8d7afeb9603f8289e5704755c7e5c3bbd929c90c817164b", size = 352548, upload-time = "2025-07-26T12:02:05.165Z" }, + { url = "https://files.pythonhosted.org/packages/32/1d/a209ec1a3a3452d490f6b14dd92e72280c99ae3d1e73da74f8277d4ee08f/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:4feffb6537d64b84877da813a5c30f1422ea5739566abf0bd18065ac040e120a", size = 1322297, upload-time = "2025-07-26T12:02:07.379Z" }, + { url = "https://files.pythonhosted.org/packages/bc/9e/46f0e8ebdd884ca0e8877e46a3f4e633f6c9c8c4f3f6e72be3fe075994aa/contourpy-1.3.3-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:2b7e9480ffe2b0cd2e787e4df64270e3a0440d9db8dc823312e2c940c167df7e", size = 1391023, upload-time = "2025-07-26T12:02:10.171Z" }, + { url = "https://files.pythonhosted.org/packages/b9/70/f308384a3ae9cd2209e0849f33c913f658d3326900d0ff5d378d6a1422d2/contourpy-1.3.3-cp313-cp313t-win32.whl", hash = "sha256:283edd842a01e3dcd435b1c5116798d661378d83d36d337b8dde1d16a5fc9ba3", size = 196157, upload-time = "2025-07-26T12:02:11.488Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/880f890a6663b84d9e34a6f88cded89d78f0091e0045a284427cb6b18521/contourpy-1.3.3-cp313-cp313t-win_amd64.whl", hash = "sha256:87acf5963fc2b34825e5b6b048f40e3635dd547f590b04d2ab317c2619ef7ae8", size = 240570, upload-time = "2025-07-26T12:02:12.754Z" }, + { url = "https://files.pythonhosted.org/packages/80/99/2adc7d8ffead633234817ef8e9a87115c8a11927a94478f6bb3d3f4d4f7d/contourpy-1.3.3-cp313-cp313t-win_arm64.whl", hash = "sha256:3c30273eb2a55024ff31ba7d052dde990d7d8e5450f4bbb6e913558b3d6c2301", size = 199713, upload-time = "2025-07-26T12:02:14.4Z" }, + { url = "https://files.pythonhosted.org/packages/72/8b/4546f3ab60f78c514ffb7d01a0bd743f90de36f0019d1be84d0a708a580a/contourpy-1.3.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fde6c716d51c04b1c25d0b90364d0be954624a0ee9d60e23e850e8d48353d07a", size = 292189, upload-time = "2025-07-26T12:02:16.095Z" }, + { url = "https://files.pythonhosted.org/packages/fd/e1/3542a9cb596cadd76fcef413f19c79216e002623158befe6daa03dbfa88c/contourpy-1.3.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:cbedb772ed74ff5be440fa8eee9bd49f64f6e3fc09436d9c7d8f1c287b121d77", size = 273251, upload-time = "2025-07-26T12:02:17.524Z" }, + { url = "https://files.pythonhosted.org/packages/b1/71/f93e1e9471d189f79d0ce2497007731c1e6bf9ef6d1d61b911430c3db4e5/contourpy-1.3.3-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22e9b1bd7a9b1d652cd77388465dc358dafcd2e217d35552424aa4f996f524f5", size = 335810, upload-time = "2025-07-26T12:02:18.9Z" }, + { url = "https://files.pythonhosted.org/packages/91/f9/e35f4c1c93f9275d4e38681a80506b5510e9327350c51f8d4a5a724d178c/contourpy-1.3.3-cp314-cp314-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a22738912262aa3e254e4f3cb079a95a67132fc5a063890e224393596902f5a4", size = 382871, upload-time = "2025-07-26T12:02:20.418Z" }, + { url = "https://files.pythonhosted.org/packages/b5/71/47b512f936f66a0a900d81c396a7e60d73419868fba959c61efed7a8ab46/contourpy-1.3.3-cp314-cp314-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:afe5a512f31ee6bd7d0dda52ec9864c984ca3d66664444f2d72e0dc4eb832e36", size = 386264, upload-time = "2025-07-26T12:02:21.916Z" }, + { url = "https://files.pythonhosted.org/packages/04/5f/9ff93450ba96b09c7c2b3f81c94de31c89f92292f1380261bd7195bea4ea/contourpy-1.3.3-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f64836de09927cba6f79dcd00fdd7d5329f3fccc633468507079c829ca4db4e3", size = 363819, upload-time = "2025-07-26T12:02:23.759Z" }, + { url = "https://files.pythonhosted.org/packages/3e/a6/0b185d4cc480ee494945cde102cb0149ae830b5fa17bf855b95f2e70ad13/contourpy-1.3.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:1fd43c3be4c8e5fd6e4f2baeae35ae18176cf2e5cced681cca908addf1cdd53b", size = 1333650, upload-time = "2025-07-26T12:02:26.181Z" }, + { url = "https://files.pythonhosted.org/packages/43/d7/afdc95580ca56f30fbcd3060250f66cedbde69b4547028863abd8aa3b47e/contourpy-1.3.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6afc576f7b33cf00996e5c1102dc2a8f7cc89e39c0b55df93a0b78c1bd992b36", size = 1404833, upload-time = "2025-07-26T12:02:28.782Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/366af18a6d386f41132a48f033cbd2102e9b0cf6345d35ff0826cd984566/contourpy-1.3.3-cp314-cp314-win32.whl", hash = "sha256:66c8a43a4f7b8df8b71ee1840e4211a3c8d93b214b213f590e18a1beca458f7d", size = 189692, upload-time = "2025-07-26T12:02:30.128Z" }, + { url = "https://files.pythonhosted.org/packages/7d/c2/57f54b03d0f22d4044b8afb9ca0e184f8b1afd57b4f735c2fa70883dc601/contourpy-1.3.3-cp314-cp314-win_amd64.whl", hash = "sha256:cf9022ef053f2694e31d630feaacb21ea24224be1c3ad0520b13d844274614fd", size = 232424, upload-time = "2025-07-26T12:02:31.395Z" }, + { url = "https://files.pythonhosted.org/packages/18/79/a9416650df9b525737ab521aa181ccc42d56016d2123ddcb7b58e926a42c/contourpy-1.3.3-cp314-cp314-win_arm64.whl", hash = "sha256:95b181891b4c71de4bb404c6621e7e2390745f887f2a026b2d99e92c17892339", size = 198300, upload-time = "2025-07-26T12:02:32.956Z" }, + { url = "https://files.pythonhosted.org/packages/1f/42/38c159a7d0f2b7b9c04c64ab317042bb6952b713ba875c1681529a2932fe/contourpy-1.3.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:33c82d0138c0a062380332c861387650c82e4cf1747aaa6938b9b6516762e772", size = 306769, upload-time = "2025-07-26T12:02:34.2Z" }, + { url = "https://files.pythonhosted.org/packages/c3/6c/26a8205f24bca10974e77460de68d3d7c63e282e23782f1239f226fcae6f/contourpy-1.3.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ea37e7b45949df430fe649e5de8351c423430046a2af20b1c1961cae3afcda77", size = 287892, upload-time = "2025-07-26T12:02:35.807Z" }, + { url = "https://files.pythonhosted.org/packages/66/06/8a475c8ab718ebfd7925661747dbb3c3ee9c82ac834ccb3570be49d129f4/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d304906ecc71672e9c89e87c4675dc5c2645e1f4269a5063b99b0bb29f232d13", size = 326748, upload-time = "2025-07-26T12:02:37.193Z" }, + { url = "https://files.pythonhosted.org/packages/b4/a3/c5ca9f010a44c223f098fccd8b158bb1cb287378a31ac141f04730dc49be/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca658cd1a680a5c9ea96dc61cdbae1e85c8f25849843aa799dfd3cb370ad4fbe", size = 375554, upload-time = "2025-07-26T12:02:38.894Z" }, + { url = "https://files.pythonhosted.org/packages/80/5b/68bd33ae63fac658a4145088c1e894405e07584a316738710b636c6d0333/contourpy-1.3.3-cp314-cp314t-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:ab2fd90904c503739a75b7c8c5c01160130ba67944a7b77bbf36ef8054576e7f", size = 388118, upload-time = "2025-07-26T12:02:40.642Z" }, + { url = "https://files.pythonhosted.org/packages/40/52/4c285a6435940ae25d7410a6c36bda5145839bc3f0beb20c707cda18b9d2/contourpy-1.3.3-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b7301b89040075c30e5768810bc96a8e8d78085b47d8be6e4c3f5a0b4ed478a0", size = 352555, upload-time = "2025-07-26T12:02:42.25Z" }, + { url = "https://files.pythonhosted.org/packages/24/ee/3e81e1dd174f5c7fefe50e85d0892de05ca4e26ef1c9a59c2a57e43b865a/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2a2a8b627d5cc6b7c41a4beff6c5ad5eb848c88255fda4a8745f7e901b32d8e4", size = 1322295, upload-time = "2025-07-26T12:02:44.668Z" }, + { url = "https://files.pythonhosted.org/packages/3c/b2/6d913d4d04e14379de429057cd169e5e00f6c2af3bb13e1710bcbdb5da12/contourpy-1.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:fd6ec6be509c787f1caf6b247f0b1ca598bef13f4ddeaa126b7658215529ba0f", size = 1391027, upload-time = "2025-07-26T12:02:47.09Z" }, + { url = "https://files.pythonhosted.org/packages/93/8a/68a4ec5c55a2971213d29a9374913f7e9f18581945a7a31d1a39b5d2dfe5/contourpy-1.3.3-cp314-cp314t-win32.whl", hash = "sha256:e74a9a0f5e3fff48fb5a7f2fd2b9b70a3fe014a67522f79b7cca4c0c7e43c9ae", size = 202428, upload-time = "2025-07-26T12:02:48.691Z" }, + { url = "https://files.pythonhosted.org/packages/fa/96/fd9f641ffedc4fa3ace923af73b9d07e869496c9cc7a459103e6e978992f/contourpy-1.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:13b68d6a62db8eafaebb8039218921399baf6e47bf85006fd8529f2a08ef33fc", size = 250331, upload-time = "2025-07-26T12:02:50.137Z" }, + { url = "https://files.pythonhosted.org/packages/ae/8c/469afb6465b853afff216f9528ffda78a915ff880ed58813ba4faf4ba0b6/contourpy-1.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:b7448cb5a725bb1e35ce88771b86fba35ef418952474492cf7c764059933ff8b", size = 203831, upload-time = "2025-07-26T12:02:51.449Z" }, + { url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" }, + { url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" }, + { url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" }, + { url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/e9/6f/6cc6cc9955caa6eaf83660b0da2b077c7fe8ff9950a3c5e45d605038d439/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:bc84e875994c3b445871ea7181d424588171efec3e185dced958dad9e001950a", size = 4218321, upload-time = "2026-02-10T19:18:22.349Z" }, + { url = "https://files.pythonhosted.org/packages/3e/5d/c4da701939eeee699566a6c1367427ab91a8b7088cc2328c09dbee940415/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:2ae6971afd6246710480e3f15824ed3029a60fc16991db250034efd0b9fb4356", size = 4381786, upload-time = "2026-02-10T19:18:24.529Z" }, + { url = "https://files.pythonhosted.org/packages/ac/97/a538654732974a94ff96c1db621fa464f455c02d4bb7d2652f4edc21d600/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d861ee9e76ace6cf36a6a89b959ec08e7bc2493ee39d07ffe5acb23ef46d27da", size = 4217990, upload-time = "2026-02-10T19:18:25.957Z" }, + { url = "https://files.pythonhosted.org/packages/ae/11/7e500d2dd3ba891197b9efd2da5454b74336d64a7cc419aa7327ab74e5f6/cryptography-46.0.5-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:2b7a67c9cd56372f3249b39699f2ad479f6991e62ea15800973b956f4b73e257", size = 4381252, upload-time = "2026-02-10T19:18:27.496Z" }, +] + +[[package]] +name = "cycler" +version = "0.12.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" }, +] + +[[package]] +name = "decorator" +version = "5.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/43/fa/6d96a0978d19e17b68d634497769987b16c8f4cd0a7a05048bec693caa6b/decorator-5.2.1.tar.gz", hash = "sha256:65f266143752f734b0a7cc83c46f4618af75b8c5911b00ccb61d0ac9b6da0360", size = 56711, upload-time = "2025-02-24T04:41:34.073Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4e/8c/f3147f5c4b73e7550fe5f9352eaa956ae838d5c51eb58e7a25b9f3e2643b/decorator-5.2.1-py3-none-any.whl", hash = "sha256:d316bb415a2d9e2d2b3abcc4084c6502fc09240e292cd76a76afc106a1c8e04a", size = 9190, upload-time = "2025-02-24T04:41:32.565Z" }, +] + +[[package]] +name = "dependency-groups" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/62/55/f054de99871e7beb81935dea8a10b90cd5ce42122b1c3081d5282fdb3621/dependency_groups-1.3.1.tar.gz", hash = "sha256:78078301090517fd938c19f64a53ce98c32834dfe0dee6b88004a569a6adfefd", size = 10093, upload-time = "2025-05-02T00:34:29.452Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/99/c7/d1ec24fb280caa5a79b6b950db565dab30210a66259d17d5bb2b3a9f878d/dependency_groups-1.3.1-py3-none-any.whl", hash = "sha256:51aeaa0dfad72430fcfb7bcdbefbd75f3792e5919563077f30bc0d73f4493030", size = 8664, upload-time = "2025-05-02T00:34:27.085Z" }, +] + +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + +[[package]] +name = "exceptiongroup" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8a/0e/97c33bf5009bdbac74fd2beace167cab3f978feb69cc36f1ef79360d6c4e/exceptiongroup-1.3.1-py3-none-any.whl", hash = "sha256:a7a39a3bd276781e98394987d3a5701d0c4edffb633bb7a5144577f82c773598", size = 16740, upload-time = "2025-11-21T23:01:53.443Z" }, +] + +[[package]] +name = "executing" +version = "2.2.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/28/c14e053b6762b1044f34a13aab6859bbf40456d37d23aa286ac24cfd9a5d/executing-2.2.1.tar.gz", hash = "sha256:3632cc370565f6648cc328b32435bd120a1e4ebb20c77e3fdde9a13cd1e533c4", size = 1129488, upload-time = "2025-09-01T09:48:10.866Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, +] + +[[package]] +name = "filelock" +version = "3.19.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/40/bb/0ab3e58d22305b6f5440629d20683af28959bf793d98d11950e305c1c326/filelock-3.19.1.tar.gz", hash = "sha256:66eda1888b0171c998b35be2bcc0f6d75c388a7ce20c3f3f37aa8e96c2dddf58", size = 17687, upload-time = "2025-08-14T16:56:03.016Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/14/42b2651a2f46b022ccd948bca9f2d5af0fd8929c4eec235b8d6d844fbe67/filelock-3.19.1-py3-none-any.whl", hash = "sha256:d38e30481def20772f5baf097c122c3babc4fcdb7e14e57049eb9d88c6dc017d", size = 15988, upload-time = "2025-08-14T16:56:01.633Z" }, +] + +[[package]] +name = "filelock" +version = "3.24.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/73/92/a8e2479937ff39185d20dd6a851c1a63e55849e447a55e798cc2e1f49c65/filelock-3.24.3.tar.gz", hash = "sha256:011a5644dc937c22699943ebbfc46e969cdde3e171470a6e40b9533e5a72affa", size = 37935, upload-time = "2026-02-19T00:48:20.543Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/0f/5d0c71a1aefeb08efff26272149e07ab922b64f46c63363756224bd6872e/filelock-3.24.3-py3-none-any.whl", hash = "sha256:426e9a4660391f7f8a810d71b0555bce9008b0a1cc342ab1f6947d37639e002d", size = 24331, upload-time = "2026-02-19T00:48:18.465Z" }, +] + +[[package]] +name = "fonttools" +version = "4.60.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/3e/c4/db6a7b5eb0656534c3aa2596c2c5e18830d74f1b9aa5aa8a7dff63a0b11d/fonttools-4.60.2.tar.gz", hash = "sha256:d29552e6b155ebfc685b0aecf8d429cb76c14ab734c22ef5d3dea6fdf800c92c", size = 3562254, upload-time = "2025-12-09T13:38:11.835Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ab/de/9e10a99fb3070accb8884886a41a4ce54e49bf2fa4fc63f48a6cf2061713/fonttools-4.60.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:4e36fadcf7e8ca6e34d490eef86ed638d6fd9c55d2f514b05687622cfc4a7050", size = 2850403, upload-time = "2025-12-09T13:35:53.14Z" }, + { url = "https://files.pythonhosted.org/packages/e4/40/d5b369d1073b134f600a94a287e13b5bdea2191ba6347d813fa3da00e94a/fonttools-4.60.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6e500fc9c04bee749ceabfc20cb4903f6981c2139050d85720ea7ada61b75d5c", size = 2398629, upload-time = "2025-12-09T13:35:56.471Z" }, + { url = "https://files.pythonhosted.org/packages/7c/b5/123819369aaf99d1e4dc49f1de1925d4edc7379114d15a56a7dd2e9d56e6/fonttools-4.60.2-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:22efea5e784e1d1cd8d7b856c198e360a979383ebc6dea4604743b56da1cbc34", size = 4893471, upload-time = "2025-12-09T13:35:58.927Z" }, + { url = "https://files.pythonhosted.org/packages/24/29/f8f8acccb9716b899be4be45e9ce770d6aa76327573863e68448183091b0/fonttools-4.60.2-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:677aa92d84d335e4d301d8ba04afca6f575316bc647b6782cb0921943fcb6343", size = 4854686, upload-time = "2025-12-09T13:36:01.767Z" }, + { url = "https://files.pythonhosted.org/packages/5a/0d/f3f51d7519f44f2dd5c9a60d7cd41185ebcee4348f073e515a3a93af15ff/fonttools-4.60.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:edd49d3defbf35476e78b61ff737ff5efea811acff68d44233a95a5a48252334", size = 4871233, upload-time = "2025-12-09T13:36:06.094Z" }, + { url = "https://files.pythonhosted.org/packages/cc/3f/4d4fd47d3bc40ab4d76718555185f8adffb5602ea572eac4bbf200c47d22/fonttools-4.60.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:126839492b69cecc5baf2bddcde60caab2ffafd867bbae2a88463fce6078ca3a", size = 4988936, upload-time = "2025-12-09T13:36:08.42Z" }, + { url = "https://files.pythonhosted.org/packages/01/6f/83bbdefa43f2c3ae206fd8c4b9a481f3c913eef871b1ce9a453069239e39/fonttools-4.60.2-cp310-cp310-win32.whl", hash = "sha256:ffcab6f5537136046ca902ed2491ab081ba271b07591b916289b7c27ff845f96", size = 2278044, upload-time = "2025-12-09T13:36:10.641Z" }, + { url = "https://files.pythonhosted.org/packages/d4/04/7d9a137e919d6c9ef26704b7f7b2580d9cfc5139597588227aacebc0e3b7/fonttools-4.60.2-cp310-cp310-win_amd64.whl", hash = "sha256:9c68b287c7ffcd29dd83b5f961004b2a54a862a88825d52ea219c6220309ba45", size = 2326522, upload-time = "2025-12-09T13:36:12.981Z" }, + { url = "https://files.pythonhosted.org/packages/e0/80/b7693d37c02417e162cc83cdd0b19a4f58be82c638b5d4ce4de2dae050c4/fonttools-4.60.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:a2aed0a7931401b3875265717a24c726f87ecfedbb7b3426c2ca4d2812e281ae", size = 2847809, upload-time = "2025-12-09T13:36:14.884Z" }, + { url = "https://files.pythonhosted.org/packages/f9/9a/9c2c13bf8a6496ac21607d704e74e9cc68ebf23892cf924c9a8b5c7566b9/fonttools-4.60.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:dea6868e9d2b816c9076cfea77754686f3c19149873bdbc5acde437631c15df1", size = 2397302, upload-time = "2025-12-09T13:36:17.151Z" }, + { url = "https://files.pythonhosted.org/packages/56/f6/ce38ff6b2d2d58f6fd981d32f3942365bfa30eadf2b47d93b2d48bf6097f/fonttools-4.60.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2fa27f34950aa1fe0f0b1abe25eed04770a3b3b34ad94e5ace82cc341589678a", size = 5054418, upload-time = "2025-12-09T13:36:19.062Z" }, + { url = "https://files.pythonhosted.org/packages/88/06/5353bea128ff39e857c31de3dd605725b4add956badae0b31bc9a50d4c8e/fonttools-4.60.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:13a53d479d187b09bfaa4a35ffcbc334fc494ff355f0a587386099cb66674f1e", size = 5031652, upload-time = "2025-12-09T13:36:21.206Z" }, + { url = "https://files.pythonhosted.org/packages/71/05/ebca836437f6ebd57edd6428e7eff584e683ff0556ddb17d62e3b731f46c/fonttools-4.60.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fac5e921d3bd0ca3bb8517dced2784f0742bc8ca28579a68b139f04ea323a779", size = 5030321, upload-time = "2025-12-09T13:36:23.515Z" }, + { url = "https://files.pythonhosted.org/packages/57/f9/eb9d2a2ce30c99f840c1cc3940729a970923cf39d770caf88909d98d516b/fonttools-4.60.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:648f4f9186fd7f1f3cd57dbf00d67a583720d5011feca67a5e88b3a491952cfb", size = 5154255, upload-time = "2025-12-09T13:36:25.879Z" }, + { url = "https://files.pythonhosted.org/packages/08/a2/088b6ceba8272a9abb629d3c08f9c1e35e5ce42db0ccfe0c1f9f03e60d1d/fonttools-4.60.2-cp311-cp311-win32.whl", hash = "sha256:3274e15fad871bead5453d5ce02658f6d0c7bc7e7021e2a5b8b04e2f9e40da1a", size = 2276300, upload-time = "2025-12-09T13:36:27.772Z" }, + { url = "https://files.pythonhosted.org/packages/de/2f/8e4c3d908cc5dade7bb1316ce48589f6a24460c1056fd4b8db51f1fa309a/fonttools-4.60.2-cp311-cp311-win_amd64.whl", hash = "sha256:91d058d5a483a1525b367803abb69de0923fbd45e1f82ebd000f5c8aa65bc78e", size = 2327574, upload-time = "2025-12-09T13:36:30.89Z" }, + { url = "https://files.pythonhosted.org/packages/c0/30/530c9eddcd1c39219dc0aaede2b5a4c8ab80e0bb88d1b3ffc12944c4aac3/fonttools-4.60.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e0164b7609d2b5c5dd4e044b8085b7bd7ca7363ef8c269a4ab5b5d4885a426b2", size = 2847196, upload-time = "2025-12-09T13:36:33.262Z" }, + { url = "https://files.pythonhosted.org/packages/19/2f/4077a482836d5bbe3bc9dac1c004d02ee227cf04ed62b0a2dfc41d4f0dfd/fonttools-4.60.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:1dd3d9574fc595c1e97faccae0f264dc88784ddf7fbf54c939528378bacc0033", size = 2395842, upload-time = "2025-12-09T13:36:35.47Z" }, + { url = "https://files.pythonhosted.org/packages/dd/05/aae5bb99c5398f8ed4a8b784f023fd9dd3568f0bd5d5b21e35b282550f11/fonttools-4.60.2-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:98d0719f1b11c2817307d2da2e94296a3b2a3503f8d6252a101dca3ee663b917", size = 4949713, upload-time = "2025-12-09T13:36:37.874Z" }, + { url = "https://files.pythonhosted.org/packages/b4/37/49067349fc78ff0efbf09fadefe80ddf41473ca8f8a25400e3770da38328/fonttools-4.60.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d3ea26957dd07209f207b4fff64c702efe5496de153a54d3b91007ec28904dd", size = 4999907, upload-time = "2025-12-09T13:36:39.853Z" }, + { url = "https://files.pythonhosted.org/packages/16/31/d0f11c758bd0db36b664c92a0f9dfdcc2d7313749aa7d6629805c6946f21/fonttools-4.60.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1ee301273b0850f3a515299f212898f37421f42ff9adfc341702582ca5073c13", size = 4939717, upload-time = "2025-12-09T13:36:43.075Z" }, + { url = "https://files.pythonhosted.org/packages/d9/bc/1cff0d69522e561bf1b99bee7c3911c08c25e919584827c3454a64651ce9/fonttools-4.60.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c6eb4694cc3b9c03b7c01d65a9cf35b577f21aa6abdbeeb08d3114b842a58153", size = 5089205, upload-time = "2025-12-09T13:36:45.468Z" }, + { url = "https://files.pythonhosted.org/packages/05/e6/fb174f0069b7122e19828c551298bfd34fdf9480535d2a6ac2ed37afacd3/fonttools-4.60.2-cp312-cp312-win32.whl", hash = "sha256:57f07b616c69c244cc1a5a51072eeef07dddda5ebef9ca5c6e9cf6d59ae65b70", size = 2264674, upload-time = "2025-12-09T13:36:49.238Z" }, + { url = "https://files.pythonhosted.org/packages/75/57/6552ffd6b582d3e6a9f01780c5275e6dfff1e70ca146101733aa1c12a129/fonttools-4.60.2-cp312-cp312-win_amd64.whl", hash = "sha256:310035802392f1fe5a7cf43d76f6ff4a24c919e4c72c0352e7b8176e2584b8a0", size = 2314701, upload-time = "2025-12-09T13:36:51.09Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e4/8381d0ca6b6c6c484660b03517ec5b5b81feeefca3808726dece36c652a9/fonttools-4.60.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2bb5fd231e56ccd7403212636dcccffc96c5ae0d6f9e4721fa0a32cb2e3ca432", size = 2842063, upload-time = "2025-12-09T13:36:53.468Z" }, + { url = "https://files.pythonhosted.org/packages/b4/2c/4367117ee8ff4f4374787a1222da0bd413d80cf3522111f727a7b8f80d1d/fonttools-4.60.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:536b5fab7b6fec78ccf59b5c59489189d9d0a8b0d3a77ed1858be59afb096696", size = 2393792, upload-time = "2025-12-09T13:36:55.742Z" }, + { url = "https://files.pythonhosted.org/packages/49/b7/a76b6dffa193869e54e32ca2f9abb0d0e66784bc8a24e6f86eb093015481/fonttools-4.60.2-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:6b9288fc38252ac86a9570f19313ecbc9ff678982e0f27c757a85f1f284d3400", size = 4924020, upload-time = "2025-12-09T13:36:58.229Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4e/0078200e2259f0061c86a74075f507d64c43dd2ab38971956a5c0012d344/fonttools-4.60.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:93fcb420791d839ef592eada2b69997c445d0ce9c969b5190f2e16828ec10607", size = 4980070, upload-time = "2025-12-09T13:37:00.311Z" }, + { url = "https://files.pythonhosted.org/packages/85/1f/d87c85a11cb84852c975251581862681e4a0c1c3bd456c648792203f311b/fonttools-4.60.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7916a381b094db4052ac284255186aebf74c5440248b78860cb41e300036f598", size = 4921411, upload-time = "2025-12-09T13:37:02.345Z" }, + { url = "https://files.pythonhosted.org/packages/75/c0/7efad650f5ed8e317c2633133ef3c64917e7adf2e4e2940c798f5d57ec6e/fonttools-4.60.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:58c8c393d5e16b15662cfc2d988491940458aa87894c662154f50c7b49440bef", size = 5063465, upload-time = "2025-12-09T13:37:04.836Z" }, + { url = "https://files.pythonhosted.org/packages/18/a8/750518c4f8cdd79393b386bc81226047ade80239e58c6c9f5dbe1fdd8ea1/fonttools-4.60.2-cp313-cp313-win32.whl", hash = "sha256:19c6e0afd8b02008caa0aa08ab896dfce5d0bcb510c49b2c499541d5cb95a963", size = 2263443, upload-time = "2025-12-09T13:37:06.762Z" }, + { url = "https://files.pythonhosted.org/packages/b8/22/026c60376f165981f80a0e90bd98a79ae3334e9d89a3d046c4d2e265c724/fonttools-4.60.2-cp313-cp313-win_amd64.whl", hash = "sha256:6a500dc59e11b2338c2dba1f8cf11a4ae8be35ec24af8b2628b8759a61457b76", size = 2313800, upload-time = "2025-12-09T13:37:08.713Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ab/7cf1f5204e1366ddf9dc5cdc2789b571feb9eebcee0e3463c3f457df5f52/fonttools-4.60.2-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:9387c532acbe323bbf2a920f132bce3c408a609d5f9dcfc6532fbc7e37f8ccbb", size = 2841690, upload-time = "2025-12-09T13:37:10.696Z" }, + { url = "https://files.pythonhosted.org/packages/00/3c/0bf83c6f863cc8b934952567fa2bf737cfcec8fc4ffb59b3f93820095f89/fonttools-4.60.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e6f1c824185b5b8fb681297f315f26ae55abb0d560c2579242feea8236b1cfef", size = 2392191, upload-time = "2025-12-09T13:37:12.954Z" }, + { url = "https://files.pythonhosted.org/packages/00/f0/40090d148b8907fbea12e9bdf1ff149f30cdf1769e3b2c3e0dbf5106b88d/fonttools-4.60.2-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:55a3129d1e4030b1a30260f1b32fe76781b585fb2111d04a988e141c09eb6403", size = 4873503, upload-time = "2025-12-09T13:37:15.142Z" }, + { url = "https://files.pythonhosted.org/packages/dc/e0/d8b13f99e58b8c293781288ba62fe634f1f0697c9c4c0ae104d3215f3a10/fonttools-4.60.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b196e63753abc33b3b97a6fd6de4b7c4fef5552c0a5ba5e562be214d1e9668e0", size = 4968493, upload-time = "2025-12-09T13:37:18.272Z" }, + { url = "https://files.pythonhosted.org/packages/46/c5/960764d12c92bc225f02401d3067048cb7b282293d9e48e39fe2b0ec38a9/fonttools-4.60.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:de76c8d740fb55745f3b154f0470c56db92ae3be27af8ad6c2e88f1458260c9a", size = 4920015, upload-time = "2025-12-09T13:37:20.334Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ab/839d8caf253d1eef3653ef4d34427d0326d17a53efaec9eb04056b670fff/fonttools-4.60.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:6ba6303225c95998c9fda2d410aa792c3d2c1390a09df58d194b03e17583fa25", size = 5031165, upload-time = "2025-12-09T13:37:23.57Z" }, + { url = "https://files.pythonhosted.org/packages/de/bf/3bc862796a6841cbe0725bb5512d272239b809dba631a4b0301df885e62d/fonttools-4.60.2-cp314-cp314-win32.whl", hash = "sha256:0a89728ce10d7c816fedaa5380c06d2793e7a8a634d7ce16810e536c22047384", size = 2267526, upload-time = "2025-12-09T13:37:25.821Z" }, + { url = "https://files.pythonhosted.org/packages/fc/a1/c1909cacf00c76dc37b4743451561fbaaf7db4172c22a6d9394081d114c3/fonttools-4.60.2-cp314-cp314-win_amd64.whl", hash = "sha256:fa8446e6ab8bd778b82cb1077058a2addba86f30de27ab9cc18ed32b34bc8667", size = 2319096, upload-time = "2025-12-09T13:37:28.058Z" }, + { url = "https://files.pythonhosted.org/packages/29/b3/f66e71433f08e3a931b2b31a665aeed17fcc5e6911fc73529c70a232e421/fonttools-4.60.2-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:4063bc81ac5a4137642865cb63dd270e37b3cd1f55a07c0d6e41d072699ccca2", size = 2925167, upload-time = "2025-12-09T13:37:30.348Z" }, + { url = "https://files.pythonhosted.org/packages/2e/13/eeb491ff743594bbd0bee6e49422c03a59fe9c49002d3cc60eeb77414285/fonttools-4.60.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:ebfdb66fa69732ed604ab8e2a0431e6deff35e933a11d73418cbc7823d03b8e1", size = 2430923, upload-time = "2025-12-09T13:37:32.817Z" }, + { url = "https://files.pythonhosted.org/packages/b2/e5/db609f785e460796e53c4dbc3874a5f4948477f27beceb5e2d24b2537666/fonttools-4.60.2-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50b10b3b1a72d1d54c61b0e59239e1a94c0958f4a06a1febf97ce75388dd91a4", size = 4877729, upload-time = "2025-12-09T13:37:35.858Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d6/85e4484dd4bfb03fee7bd370d65888cccbd3dee2681ee48c869dd5ccb23f/fonttools-4.60.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:beae16891a13b4a2ddec9b39b4de76092a3025e4d1c82362e3042b62295d5e4d", size = 5096003, upload-time = "2025-12-09T13:37:37.862Z" }, + { url = "https://files.pythonhosted.org/packages/30/49/1a98e44b71030b83d2046f981373b80571868259d98e6dae7bc20099dac6/fonttools-4.60.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:522f017fdb3766fd5d2d321774ef351cc6ce88ad4e6ac9efe643e4a2b9d528db", size = 4974410, upload-time = "2025-12-09T13:37:40.166Z" }, + { url = "https://files.pythonhosted.org/packages/42/07/d6f775d950ee8a841012472c7303f8819423d8cc3b4530915de7265ebfa2/fonttools-4.60.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:82cceceaf9c09a965a75b84a4b240dd3768e596ffb65ef53852681606fe7c9ba", size = 5002036, upload-time = "2025-12-09T13:37:42.639Z" }, + { url = "https://files.pythonhosted.org/packages/73/f6/ba6458f83ce1a9f8c3b17bd8f7b8a2205a126aac1055796b7e7cfebbd38f/fonttools-4.60.2-cp314-cp314t-win32.whl", hash = "sha256:bbfbc918a75437fe7e6d64d1b1e1f713237df1cf00f3a36dedae910b2ba01cee", size = 2330985, upload-time = "2025-12-09T13:37:45.157Z" }, + { url = "https://files.pythonhosted.org/packages/91/24/fea0ba4d3a32d4ed1103a1098bfd99dc78b5fe3bb97202920744a37b73dc/fonttools-4.60.2-cp314-cp314t-win_amd64.whl", hash = "sha256:0e5cd9b0830f6550d58c84f3ab151a9892b50c4f9d538c5603c0ce6fff2eb3f1", size = 2396226, upload-time = "2025-12-09T13:37:47.355Z" }, + { url = "https://files.pythonhosted.org/packages/55/ae/a6d9446cb258d3fe87e311c2d7bacf8e8da3e5809fbdc3a8306db4f6b14e/fonttools-4.60.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:a3c75b8b42f7f93906bdba9eb1197bb76aecbe9a0a7cf6feec75f7605b5e8008", size = 2857184, upload-time = "2025-12-09T13:37:49.96Z" }, + { url = "https://files.pythonhosted.org/packages/3a/f3/1b41d0b6a8b908aa07f652111155dd653ebbf0b3385e66562556c5206685/fonttools-4.60.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:0f86c8c37bc0ec0b9c141d5e90c717ff614e93c187f06d80f18c7057097f71bc", size = 2401877, upload-time = "2025-12-09T13:37:52.307Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/048fd781680c38b05c5463657d0d95d5f2391a51972176e175c01de29d42/fonttools-4.60.2-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fe905403fe59683b0e9a45f234af2866834376b8821f34633b1c76fb731b6311", size = 4878073, upload-time = "2025-12-09T13:37:56.477Z" }, + { url = "https://files.pythonhosted.org/packages/45/bb/363364f052a893cebd3d449588b21244a9d873620fda03ad92702d2e1bc7/fonttools-4.60.2-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38ce703b60a906e421e12d9e3a7f064883f5e61bb23e8961f4be33cfe578500b", size = 4835385, upload-time = "2025-12-09T13:37:58.882Z" }, + { url = "https://files.pythonhosted.org/packages/1c/38/e392bb930b2436287e6021672345db26441bf1f85f1e98f8b9784334e41d/fonttools-4.60.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9e810c06f3e79185cecf120e58b343ea5a89b54dd695fd644446bcf8c026da5e", size = 4853084, upload-time = "2025-12-09T13:38:01.578Z" }, + { url = "https://files.pythonhosted.org/packages/65/60/0d77faeaecf7a3276a8a6dc49e2274357e6b3ed6a1774e2fdb2a7f142db0/fonttools-4.60.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:38faec8cc1d12122599814d15a402183f5123fb7608dac956121e7c6742aebc5", size = 4971144, upload-time = "2025-12-09T13:38:03.748Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c7/6d3ac3afbcd598631bce24c3ecb919e7d0644a82fea8ddc4454312fc0be6/fonttools-4.60.2-cp39-cp39-win32.whl", hash = "sha256:80a45cf7bf659acb7b36578f300231873daba67bd3ca8cce181c73f861f14a37", size = 1499411, upload-time = "2025-12-09T13:38:05.586Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/9dedf6420e23f9fa630bb97941839dddd2e1e57d1b2b85a902378dbe0bd2/fonttools-4.60.2-cp39-cp39-win_amd64.whl", hash = "sha256:c355d5972071938e1b1e0f5a1df001f68ecf1a62f34a3407dc8e0beccf052501", size = 1547943, upload-time = "2025-12-09T13:38:07.604Z" }, + { url = "https://files.pythonhosted.org/packages/79/6c/10280af05b44fafd1dff69422805061fa1af29270bc52dce031ac69540bf/fonttools-4.60.2-py3-none-any.whl", hash = "sha256:73cf92eeda67cf6ff10c8af56fc8f4f07c1647d989a979be9e388a49be26552a", size = 1144610, upload-time = "2025-12-09T13:38:09.5Z" }, +] + +[[package]] +name = "fonttools" +version = "4.61.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/ca/cf17b88a8df95691275a3d77dc0a5ad9907f328ae53acbe6795da1b2f5ed/fonttools-4.61.1.tar.gz", hash = "sha256:6675329885c44657f826ef01d9e4fb33b9158e9d93c537d84ad8399539bc6f69", size = 3565756, upload-time = "2025-12-12T17:31:24.246Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5b/94/8a28707adb00bed1bf22dac16ccafe60faf2ade353dcb32c3617ee917307/fonttools-4.61.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c7db70d57e5e1089a274cbb2b1fd635c9a24de809a231b154965d415d6c6d24", size = 2854799, upload-time = "2025-12-12T17:29:27.5Z" }, + { url = "https://files.pythonhosted.org/packages/94/93/c2e682faaa5ee92034818d8f8a8145ae73eb83619600495dcf8503fa7771/fonttools-4.61.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5fe9fd43882620017add5eabb781ebfbc6998ee49b35bd7f8f79af1f9f99a958", size = 2403032, upload-time = "2025-12-12T17:29:30.115Z" }, + { url = "https://files.pythonhosted.org/packages/f1/62/1748f7e7e1ee41aa52279fd2e3a6d0733dc42a673b16932bad8e5d0c8b28/fonttools-4.61.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8db08051fc9e7d8bc622f2112511b8107d8f27cd89e2f64ec45e9825e8288da", size = 4897863, upload-time = "2025-12-12T17:29:32.535Z" }, + { url = "https://files.pythonhosted.org/packages/69/69/4ca02ee367d2c98edcaeb83fc278d20972502ee071214ad9d8ca85e06080/fonttools-4.61.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a76d4cb80f41ba94a6691264be76435e5f72f2cb3cab0b092a6212855f71c2f6", size = 4859076, upload-time = "2025-12-12T17:29:34.907Z" }, + { url = "https://files.pythonhosted.org/packages/8c/f5/660f9e3cefa078861a7f099107c6d203b568a6227eef163dd173bfc56bdc/fonttools-4.61.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:a13fc8aeb24bad755eea8f7f9d409438eb94e82cf86b08fe77a03fbc8f6a96b1", size = 4875623, upload-time = "2025-12-12T17:29:37.33Z" }, + { url = "https://files.pythonhosted.org/packages/63/d1/9d7c5091d2276ed47795c131c1bf9316c3c1ab2789c22e2f59e0572ccd38/fonttools-4.61.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:b846a1fcf8beadeb9ea4f44ec5bdde393e2f1569e17d700bfc49cd69bde75881", size = 4993327, upload-time = "2025-12-12T17:29:39.781Z" }, + { url = "https://files.pythonhosted.org/packages/6f/2d/28def73837885ae32260d07660a052b99f0aa00454867d33745dfe49dbf0/fonttools-4.61.1-cp310-cp310-win32.whl", hash = "sha256:78a7d3ab09dc47ac1a363a493e6112d8cabed7ba7caad5f54dbe2f08676d1b47", size = 1502180, upload-time = "2025-12-12T17:29:42.217Z" }, + { url = "https://files.pythonhosted.org/packages/63/fa/bfdc98abb4dd2bd491033e85e3ba69a2313c850e759a6daa014bc9433b0f/fonttools-4.61.1-cp310-cp310-win_amd64.whl", hash = "sha256:eff1ac3cc66c2ac7cda1e64b4e2f3ffef474b7335f92fc3833fc632d595fcee6", size = 1550654, upload-time = "2025-12-12T17:29:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/69/12/bf9f4eaa2fad039356cc627587e30ed008c03f1cebd3034376b5ee8d1d44/fonttools-4.61.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c6604b735bb12fef8e0efd5578c9fb5d3d8532d5001ea13a19cddf295673ee09", size = 2852213, upload-time = "2025-12-12T17:29:46.675Z" }, + { url = "https://files.pythonhosted.org/packages/ac/49/4138d1acb6261499bedde1c07f8c2605d1d8f9d77a151e5507fd3ef084b6/fonttools-4.61.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5ce02f38a754f207f2f06557523cd39a06438ba3aafc0639c477ac409fc64e37", size = 2401689, upload-time = "2025-12-12T17:29:48.769Z" }, + { url = "https://files.pythonhosted.org/packages/e5/fe/e6ce0fe20a40e03aef906af60aa87668696f9e4802fa283627d0b5ed777f/fonttools-4.61.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:77efb033d8d7ff233385f30c62c7c79271c8885d5c9657d967ede124671bbdfb", size = 5058809, upload-time = "2025-12-12T17:29:51.701Z" }, + { url = "https://files.pythonhosted.org/packages/79/61/1ca198af22f7dd22c17ab86e9024ed3c06299cfdb08170640e9996d501a0/fonttools-4.61.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:75c1a6dfac6abd407634420c93864a1e274ebc1c7531346d9254c0d8f6ca00f9", size = 5036039, upload-time = "2025-12-12T17:29:53.659Z" }, + { url = "https://files.pythonhosted.org/packages/99/cc/fa1801e408586b5fce4da9f5455af8d770f4fc57391cd5da7256bb364d38/fonttools-4.61.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:0de30bfe7745c0d1ffa2b0b7048fb7123ad0d71107e10ee090fa0b16b9452e87", size = 5034714, upload-time = "2025-12-12T17:29:55.592Z" }, + { url = "https://files.pythonhosted.org/packages/bf/aa/b7aeafe65adb1b0a925f8f25725e09f078c635bc22754f3fecb7456955b0/fonttools-4.61.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:58b0ee0ab5b1fc9921eccfe11d1435added19d6494dde14e323f25ad2bc30c56", size = 5158648, upload-time = "2025-12-12T17:29:57.861Z" }, + { url = "https://files.pythonhosted.org/packages/99/f9/08ea7a38663328881384c6e7777bbefc46fd7d282adfd87a7d2b84ec9d50/fonttools-4.61.1-cp311-cp311-win32.whl", hash = "sha256:f79b168428351d11e10c5aeb61a74e1851ec221081299f4cf56036a95431c43a", size = 2280681, upload-time = "2025-12-12T17:29:59.943Z" }, + { url = "https://files.pythonhosted.org/packages/07/ad/37dd1ae5fa6e01612a1fbb954f0927681f282925a86e86198ccd7b15d515/fonttools-4.61.1-cp311-cp311-win_amd64.whl", hash = "sha256:fe2efccb324948a11dd09d22136fe2ac8a97d6c1347cf0b58a911dcd529f66b7", size = 2331951, upload-time = "2025-12-12T17:30:02.254Z" }, + { url = "https://files.pythonhosted.org/packages/6f/16/7decaa24a1bd3a70c607b2e29f0adc6159f36a7e40eaba59846414765fd4/fonttools-4.61.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:f3cb4a569029b9f291f88aafc927dd53683757e640081ca8c412781ea144565e", size = 2851593, upload-time = "2025-12-12T17:30:04.225Z" }, + { url = "https://files.pythonhosted.org/packages/94/98/3c4cb97c64713a8cf499b3245c3bf9a2b8fd16a3e375feff2aed78f96259/fonttools-4.61.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41a7170d042e8c0024703ed13b71893519a1a6d6e18e933e3ec7507a2c26a4b2", size = 2400231, upload-time = "2025-12-12T17:30:06.47Z" }, + { url = "https://files.pythonhosted.org/packages/b7/37/82dbef0f6342eb01f54bca073ac1498433d6ce71e50c3c3282b655733b31/fonttools-4.61.1-cp312-cp312-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:10d88e55330e092940584774ee5e8a6971b01fc2f4d3466a1d6c158230880796", size = 4954103, upload-time = "2025-12-12T17:30:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/6c/44/f3aeac0fa98e7ad527f479e161aca6c3a1e47bb6996b053d45226fe37bf2/fonttools-4.61.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:15acc09befd16a0fb8a8f62bc147e1a82817542d72184acca9ce6e0aeda9fa6d", size = 5004295, upload-time = "2025-12-12T17:30:10.56Z" }, + { url = "https://files.pythonhosted.org/packages/14/e8/7424ced75473983b964d09f6747fa09f054a6d656f60e9ac9324cf40c743/fonttools-4.61.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e6bcdf33aec38d16508ce61fd81838f24c83c90a1d1b8c68982857038673d6b8", size = 4944109, upload-time = "2025-12-12T17:30:12.874Z" }, + { url = "https://files.pythonhosted.org/packages/c8/8b/6391b257fa3d0b553d73e778f953a2f0154292a7a7a085e2374b111e5410/fonttools-4.61.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5fade934607a523614726119164ff621e8c30e8fa1ffffbbd358662056ba69f0", size = 5093598, upload-time = "2025-12-12T17:30:15.79Z" }, + { url = "https://files.pythonhosted.org/packages/d9/71/fd2ea96cdc512d92da5678a1c98c267ddd4d8c5130b76d0f7a80f9a9fde8/fonttools-4.61.1-cp312-cp312-win32.whl", hash = "sha256:75da8f28eff26defba42c52986de97b22106cb8f26515b7c22443ebc9c2d3261", size = 2269060, upload-time = "2025-12-12T17:30:18.058Z" }, + { url = "https://files.pythonhosted.org/packages/80/3b/a3e81b71aed5a688e89dfe0e2694b26b78c7d7f39a5ffd8a7d75f54a12a8/fonttools-4.61.1-cp312-cp312-win_amd64.whl", hash = "sha256:497c31ce314219888c0e2fce5ad9178ca83fe5230b01a5006726cdf3ac9f24d9", size = 2319078, upload-time = "2025-12-12T17:30:22.862Z" }, + { url = "https://files.pythonhosted.org/packages/4b/cf/00ba28b0990982530addb8dc3e9e6f2fa9cb5c20df2abdda7baa755e8fe1/fonttools-4.61.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8c56c488ab471628ff3bfa80964372fc13504ece601e0d97a78ee74126b2045c", size = 2846454, upload-time = "2025-12-12T17:30:24.938Z" }, + { url = "https://files.pythonhosted.org/packages/5a/ca/468c9a8446a2103ae645d14fee3f610567b7042aba85031c1c65e3ef7471/fonttools-4.61.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:dc492779501fa723b04d0ab1f5be046797fee17d27700476edc7ee9ae535a61e", size = 2398191, upload-time = "2025-12-12T17:30:27.343Z" }, + { url = "https://files.pythonhosted.org/packages/a3/4b/d67eedaed19def5967fade3297fed8161b25ba94699efc124b14fb68cdbc/fonttools-4.61.1-cp313-cp313-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:64102ca87e84261419c3747a0d20f396eb024bdbeb04c2bfb37e2891f5fadcb5", size = 4928410, upload-time = "2025-12-12T17:30:29.771Z" }, + { url = "https://files.pythonhosted.org/packages/b0/8d/6fb3494dfe61a46258cd93d979cf4725ded4eb46c2a4ca35e4490d84daea/fonttools-4.61.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c1b526c8d3f615a7b1867f38a9410849c8f4aef078535742198e942fba0e9bd", size = 4984460, upload-time = "2025-12-12T17:30:32.073Z" }, + { url = "https://files.pythonhosted.org/packages/f7/f1/a47f1d30b3dc00d75e7af762652d4cbc3dff5c2697a0dbd5203c81afd9c3/fonttools-4.61.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:41ed4b5ec103bd306bb68f81dc166e77409e5209443e5773cb4ed837bcc9b0d3", size = 4925800, upload-time = "2025-12-12T17:30:34.339Z" }, + { url = "https://files.pythonhosted.org/packages/a7/01/e6ae64a0981076e8a66906fab01539799546181e32a37a0257b77e4aa88b/fonttools-4.61.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b501c862d4901792adaec7c25b1ecc749e2662543f68bb194c42ba18d6eec98d", size = 5067859, upload-time = "2025-12-12T17:30:36.593Z" }, + { url = "https://files.pythonhosted.org/packages/73/aa/28e40b8d6809a9b5075350a86779163f074d2b617c15d22343fce81918db/fonttools-4.61.1-cp313-cp313-win32.whl", hash = "sha256:4d7092bb38c53bbc78e9255a59158b150bcdc115a1e3b3ce0b5f267dc35dd63c", size = 2267821, upload-time = "2025-12-12T17:30:38.478Z" }, + { url = "https://files.pythonhosted.org/packages/1a/59/453c06d1d83dc0951b69ef692d6b9f1846680342927df54e9a1ca91c6f90/fonttools-4.61.1-cp313-cp313-win_amd64.whl", hash = "sha256:21e7c8d76f62ab13c9472ccf74515ca5b9a761d1bde3265152a6dc58700d895b", size = 2318169, upload-time = "2025-12-12T17:30:40.951Z" }, + { url = "https://files.pythonhosted.org/packages/32/8f/4e7bf82c0cbb738d3c2206c920ca34ca74ef9dabde779030145d28665104/fonttools-4.61.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:fff4f534200a04b4a36e7ae3cb74493afe807b517a09e99cb4faa89a34ed6ecd", size = 2846094, upload-time = "2025-12-12T17:30:43.511Z" }, + { url = "https://files.pythonhosted.org/packages/71/09/d44e45d0a4f3a651f23a1e9d42de43bc643cce2971b19e784cc67d823676/fonttools-4.61.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:d9203500f7c63545b4ce3799319fe4d9feb1a1b89b28d3cb5abd11b9dd64147e", size = 2396589, upload-time = "2025-12-12T17:30:45.681Z" }, + { url = "https://files.pythonhosted.org/packages/89/18/58c64cafcf8eb677a99ef593121f719e6dcbdb7d1c594ae5a10d4997ca8a/fonttools-4.61.1-cp314-cp314-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fa646ecec9528bef693415c79a86e733c70a4965dd938e9a226b0fc64c9d2e6c", size = 4877892, upload-time = "2025-12-12T17:30:47.709Z" }, + { url = "https://files.pythonhosted.org/packages/8a/ec/9e6b38c7ba1e09eb51db849d5450f4c05b7e78481f662c3b79dbde6f3d04/fonttools-4.61.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:11f35ad7805edba3aac1a3710d104592df59f4b957e30108ae0ba6c10b11dd75", size = 4972884, upload-time = "2025-12-12T17:30:49.656Z" }, + { url = "https://files.pythonhosted.org/packages/5e/87/b5339da8e0256734ba0dbbf5b6cdebb1dd79b01dc8c270989b7bcd465541/fonttools-4.61.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b931ae8f62db78861b0ff1ac017851764602288575d65b8e8ff1963fed419063", size = 4924405, upload-time = "2025-12-12T17:30:51.735Z" }, + { url = "https://files.pythonhosted.org/packages/0b/47/e3409f1e1e69c073a3a6fd8cb886eb18c0bae0ee13db2c8d5e7f8495e8b7/fonttools-4.61.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b148b56f5de675ee16d45e769e69f87623a4944f7443850bf9a9376e628a89d2", size = 5035553, upload-time = "2025-12-12T17:30:54.823Z" }, + { url = "https://files.pythonhosted.org/packages/bf/b6/1f6600161b1073a984294c6c031e1a56ebf95b6164249eecf30012bb2e38/fonttools-4.61.1-cp314-cp314-win32.whl", hash = "sha256:9b666a475a65f4e839d3d10473fad6d47e0a9db14a2f4a224029c5bfde58ad2c", size = 2271915, upload-time = "2025-12-12T17:30:57.913Z" }, + { url = "https://files.pythonhosted.org/packages/52/7b/91e7b01e37cc8eb0e1f770d08305b3655e4f002fc160fb82b3390eabacf5/fonttools-4.61.1-cp314-cp314-win_amd64.whl", hash = "sha256:4f5686e1fe5fce75d82d93c47a438a25bf0d1319d2843a926f741140b2b16e0c", size = 2323487, upload-time = "2025-12-12T17:30:59.804Z" }, + { url = "https://files.pythonhosted.org/packages/39/5c/908ad78e46c61c3e3ed70c3b58ff82ab48437faf84ec84f109592cabbd9f/fonttools-4.61.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:e76ce097e3c57c4bcb67c5aa24a0ecdbd9f74ea9219997a707a4061fbe2707aa", size = 2929571, upload-time = "2025-12-12T17:31:02.574Z" }, + { url = "https://files.pythonhosted.org/packages/bd/41/975804132c6dea64cdbfbaa59f3518a21c137a10cccf962805b301ac6ab2/fonttools-4.61.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:9cfef3ab326780c04d6646f68d4b4742aae222e8b8ea1d627c74e38afcbc9d91", size = 2435317, upload-time = "2025-12-12T17:31:04.974Z" }, + { url = "https://files.pythonhosted.org/packages/b0/5a/aef2a0a8daf1ebaae4cfd83f84186d4a72ee08fd6a8451289fcd03ffa8a4/fonttools-4.61.1-cp314-cp314t-manylinux1_x86_64.manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a75c301f96db737e1c5ed5fd7d77d9c34466de16095a266509e13da09751bd19", size = 4882124, upload-time = "2025-12-12T17:31:07.456Z" }, + { url = "https://files.pythonhosted.org/packages/80/33/d6db3485b645b81cea538c9d1c9219d5805f0877fda18777add4671c5240/fonttools-4.61.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:91669ccac46bbc1d09e9273546181919064e8df73488ea087dcac3e2968df9ba", size = 5100391, upload-time = "2025-12-12T17:31:09.732Z" }, + { url = "https://files.pythonhosted.org/packages/6c/d6/675ba631454043c75fcf76f0ca5463eac8eb0666ea1d7badae5fea001155/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c33ab3ca9d3ccd581d58e989d67554e42d8d4ded94ab3ade3508455fe70e65f7", size = 4978800, upload-time = "2025-12-12T17:31:11.681Z" }, + { url = "https://files.pythonhosted.org/packages/7f/33/d3ec753d547a8d2bdaedd390d4a814e8d5b45a093d558f025c6b990b554c/fonttools-4.61.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:664c5a68ec406f6b1547946683008576ef8b38275608e1cee6c061828171c118", size = 5006426, upload-time = "2025-12-12T17:31:13.764Z" }, + { url = "https://files.pythonhosted.org/packages/b4/40/cc11f378b561a67bea850ab50063366a0d1dd3f6d0a30ce0f874b0ad5664/fonttools-4.61.1-cp314-cp314t-win32.whl", hash = "sha256:aed04cabe26f30c1647ef0e8fbb207516fd40fe9472e9439695f5c6998e60ac5", size = 2335377, upload-time = "2025-12-12T17:31:16.49Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ff/c9a2b66b39f8628531ea58b320d66d951267c98c6a38684daa8f50fb02f8/fonttools-4.61.1-cp314-cp314t-win_amd64.whl", hash = "sha256:2180f14c141d2f0f3da43f3a81bc8aa4684860f6b0e6f9e165a4831f24e6a23b", size = 2400613, upload-time = "2025-12-12T17:31:18.769Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4e/ce75a57ff3aebf6fc1f4e9d508b8e5810618a33d900ad6c19eb30b290b97/fonttools-4.61.1-py3-none-any.whl", hash = "sha256:17d2bf5d541add43822bcf0c43d7d847b160c9bb01d15d5007d84e2217aaa371", size = 1148996, upload-time = "2025-12-12T17:31:21.03Z" }, +] + +[[package]] +name = "frozenlist" +version = "1.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2d/f5/c831fac6cc817d26fd54c7eaccd04ef7e0288806943f7cc5bbf69f3ac1f0/frozenlist-1.8.0.tar.gz", hash = "sha256:3ede829ed8d842f6cd48fc7081d7a41001a56f1f38603f9d49bf3020d59a31ad", size = 45875, upload-time = "2025-10-06T05:38:17.865Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/83/4a/557715d5047da48d54e659203b9335be7bfaafda2c3f627b7c47e0b3aaf3/frozenlist-1.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b37f6d31b3dcea7deb5e9696e529a6aa4a898adc33db82da12e4c60a7c4d2011", size = 86230, upload-time = "2025-10-06T05:35:23.699Z" }, + { url = "https://files.pythonhosted.org/packages/a2/fb/c85f9fed3ea8fe8740e5b46a59cc141c23b842eca617da8876cfce5f760e/frozenlist-1.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ef2b7b394f208233e471abc541cc6991f907ffd47dc72584acee3147899d6565", size = 49621, upload-time = "2025-10-06T05:35:25.341Z" }, + { url = "https://files.pythonhosted.org/packages/63/70/26ca3f06aace16f2352796b08704338d74b6d1a24ca38f2771afbb7ed915/frozenlist-1.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:a88f062f072d1589b7b46e951698950e7da00442fc1cacbe17e19e025dc327ad", size = 49889, upload-time = "2025-10-06T05:35:26.797Z" }, + { url = "https://files.pythonhosted.org/packages/5d/ed/c7895fd2fde7f3ee70d248175f9b6cdf792fb741ab92dc59cd9ef3bd241b/frozenlist-1.8.0-cp310-cp310-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f57fb59d9f385710aa7060e89410aeb5058b99e62f4d16b08b91986b9a2140c2", size = 219464, upload-time = "2025-10-06T05:35:28.254Z" }, + { url = "https://files.pythonhosted.org/packages/6b/83/4d587dccbfca74cb8b810472392ad62bfa100bf8108c7223eb4c4fa2f7b3/frozenlist-1.8.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:799345ab092bee59f01a915620b5d014698547afd011e691a208637312db9186", size = 221649, upload-time = "2025-10-06T05:35:29.454Z" }, + { url = "https://files.pythonhosted.org/packages/6a/c6/fd3b9cd046ec5fff9dab66831083bc2077006a874a2d3d9247dea93ddf7e/frozenlist-1.8.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c23c3ff005322a6e16f71bf8692fcf4d5a304aaafe1e262c98c6d4adc7be863e", size = 219188, upload-time = "2025-10-06T05:35:30.951Z" }, + { url = "https://files.pythonhosted.org/packages/ce/80/6693f55eb2e085fc8afb28cf611448fb5b90e98e068fa1d1b8d8e66e5c7d/frozenlist-1.8.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:8a76ea0f0b9dfa06f254ee06053d93a600865b3274358ca48a352ce4f0798450", size = 231748, upload-time = "2025-10-06T05:35:32.101Z" }, + { url = "https://files.pythonhosted.org/packages/97/d6/e9459f7c5183854abd989ba384fe0cc1a0fb795a83c033f0571ec5933ca4/frozenlist-1.8.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c7366fe1418a6133d5aa824ee53d406550110984de7637d65a178010f759c6ef", size = 236351, upload-time = "2025-10-06T05:35:33.834Z" }, + { url = "https://files.pythonhosted.org/packages/97/92/24e97474b65c0262e9ecd076e826bfd1d3074adcc165a256e42e7b8a7249/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:13d23a45c4cebade99340c4165bd90eeb4a56c6d8a9d8aa49568cac19a6d0dc4", size = 218767, upload-time = "2025-10-06T05:35:35.205Z" }, + { url = "https://files.pythonhosted.org/packages/ee/bf/dc394a097508f15abff383c5108cb8ad880d1f64a725ed3b90d5c2fbf0bb/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:e4a3408834f65da56c83528fb52ce7911484f0d1eaf7b761fc66001db1646eff", size = 235887, upload-time = "2025-10-06T05:35:36.354Z" }, + { url = "https://files.pythonhosted.org/packages/40/90/25b201b9c015dbc999a5baf475a257010471a1fa8c200c843fd4abbee725/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:42145cd2748ca39f32801dad54aeea10039da6f86e303659db90db1c4b614c8c", size = 228785, upload-time = "2025-10-06T05:35:37.949Z" }, + { url = "https://files.pythonhosted.org/packages/84/f4/b5bc148df03082f05d2dd30c089e269acdbe251ac9a9cf4e727b2dbb8a3d/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:e2de870d16a7a53901e41b64ffdf26f2fbb8917b3e6ebf398098d72c5b20bd7f", size = 230312, upload-time = "2025-10-06T05:35:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/db/4b/87e95b5d15097c302430e647136b7d7ab2398a702390cf4c8601975709e7/frozenlist-1.8.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:20e63c9493d33ee48536600d1a5c95eefc870cd71e7ab037763d1fbb89cc51e7", size = 217650, upload-time = "2025-10-06T05:35:40.377Z" }, + { url = "https://files.pythonhosted.org/packages/e5/70/78a0315d1fea97120591a83e0acd644da638c872f142fd72a6cebee825f3/frozenlist-1.8.0-cp310-cp310-win32.whl", hash = "sha256:adbeebaebae3526afc3c96fad434367cafbfd1b25d72369a9e5858453b1bb71a", size = 39659, upload-time = "2025-10-06T05:35:41.863Z" }, + { url = "https://files.pythonhosted.org/packages/66/aa/3f04523fb189a00e147e60c5b2205126118f216b0aa908035c45336e27e4/frozenlist-1.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:667c3777ca571e5dbeb76f331562ff98b957431df140b54c85fd4d52eea8d8f6", size = 43837, upload-time = "2025-10-06T05:35:43.205Z" }, + { url = "https://files.pythonhosted.org/packages/39/75/1135feecdd7c336938bd55b4dc3b0dfc46d85b9be12ef2628574b28de776/frozenlist-1.8.0-cp310-cp310-win_arm64.whl", hash = "sha256:80f85f0a7cc86e7a54c46d99c9e1318ff01f4687c172ede30fd52d19d1da1c8e", size = 39989, upload-time = "2025-10-06T05:35:44.596Z" }, + { url = "https://files.pythonhosted.org/packages/bc/03/077f869d540370db12165c0aa51640a873fb661d8b315d1d4d67b284d7ac/frozenlist-1.8.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:09474e9831bc2b2199fad6da3c14c7b0fbdd377cce9d3d77131be28906cb7d84", size = 86912, upload-time = "2025-10-06T05:35:45.98Z" }, + { url = "https://files.pythonhosted.org/packages/df/b5/7610b6bd13e4ae77b96ba85abea1c8cb249683217ef09ac9e0ae93f25a91/frozenlist-1.8.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:17c883ab0ab67200b5f964d2b9ed6b00971917d5d8a92df149dc2c9779208ee9", size = 50046, upload-time = "2025-10-06T05:35:47.009Z" }, + { url = "https://files.pythonhosted.org/packages/6e/ef/0e8f1fe32f8a53dd26bdd1f9347efe0778b0fddf62789ea683f4cc7d787d/frozenlist-1.8.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:fa47e444b8ba08fffd1c18e8cdb9a75db1b6a27f17507522834ad13ed5922b93", size = 50119, upload-time = "2025-10-06T05:35:48.38Z" }, + { url = "https://files.pythonhosted.org/packages/11/b1/71a477adc7c36e5fb628245dfbdea2166feae310757dea848d02bd0689fd/frozenlist-1.8.0-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:2552f44204b744fba866e573be4c1f9048d6a324dfe14475103fd51613eb1d1f", size = 231067, upload-time = "2025-10-06T05:35:49.97Z" }, + { url = "https://files.pythonhosted.org/packages/45/7e/afe40eca3a2dc19b9904c0f5d7edfe82b5304cb831391edec0ac04af94c2/frozenlist-1.8.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:957e7c38f250991e48a9a73e6423db1bb9dd14e722a10f6b8bb8e16a0f55f695", size = 233160, upload-time = "2025-10-06T05:35:51.729Z" }, + { url = "https://files.pythonhosted.org/packages/a6/aa/7416eac95603ce428679d273255ffc7c998d4132cfae200103f164b108aa/frozenlist-1.8.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:8585e3bb2cdea02fc88ffa245069c36555557ad3609e83be0ec71f54fd4abb52", size = 228544, upload-time = "2025-10-06T05:35:53.246Z" }, + { url = "https://files.pythonhosted.org/packages/8b/3d/2a2d1f683d55ac7e3875e4263d28410063e738384d3adc294f5ff3d7105e/frozenlist-1.8.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:edee74874ce20a373d62dc28b0b18b93f645633c2943fd90ee9d898550770581", size = 243797, upload-time = "2025-10-06T05:35:54.497Z" }, + { url = "https://files.pythonhosted.org/packages/78/1e/2d5565b589e580c296d3bb54da08d206e797d941a83a6fdea42af23be79c/frozenlist-1.8.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:c9a63152fe95756b85f31186bddf42e4c02c6321207fd6601a1c89ebac4fe567", size = 247923, upload-time = "2025-10-06T05:35:55.861Z" }, + { url = "https://files.pythonhosted.org/packages/aa/c3/65872fcf1d326a7f101ad4d86285c403c87be7d832b7470b77f6d2ed5ddc/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b6db2185db9be0a04fecf2f241c70b63b1a242e2805be291855078f2b404dd6b", size = 230886, upload-time = "2025-10-06T05:35:57.399Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/ac9ced601d62f6956f03cc794f9e04c81719509f85255abf96e2510f4265/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:f4be2e3d8bc8aabd566f8d5b8ba7ecc09249d74ba3c9ed52e54dc23a293f0b92", size = 245731, upload-time = "2025-10-06T05:35:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/b9/49/ecccb5f2598daf0b4a1415497eba4c33c1e8ce07495eb07d2860c731b8d5/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:c8d1634419f39ea6f5c427ea2f90ca85126b54b50837f31497f3bf38266e853d", size = 241544, upload-time = "2025-10-06T05:35:59.719Z" }, + { url = "https://files.pythonhosted.org/packages/53/4b/ddf24113323c0bbcc54cb38c8b8916f1da7165e07b8e24a717b4a12cbf10/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:1a7fa382a4a223773ed64242dbe1c9c326ec09457e6b8428efb4118c685c3dfd", size = 241806, upload-time = "2025-10-06T05:36:00.959Z" }, + { url = "https://files.pythonhosted.org/packages/a7/fb/9b9a084d73c67175484ba2789a59f8eebebd0827d186a8102005ce41e1ba/frozenlist-1.8.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:11847b53d722050808926e785df837353bd4d75f1d494377e59b23594d834967", size = 229382, upload-time = "2025-10-06T05:36:02.22Z" }, + { url = "https://files.pythonhosted.org/packages/95/a3/c8fb25aac55bf5e12dae5c5aa6a98f85d436c1dc658f21c3ac73f9fa95e5/frozenlist-1.8.0-cp311-cp311-win32.whl", hash = "sha256:27c6e8077956cf73eadd514be8fb04d77fc946a7fe9f7fe167648b0b9085cc25", size = 39647, upload-time = "2025-10-06T05:36:03.409Z" }, + { url = "https://files.pythonhosted.org/packages/0a/f5/603d0d6a02cfd4c8f2a095a54672b3cf967ad688a60fb9faf04fc4887f65/frozenlist-1.8.0-cp311-cp311-win_amd64.whl", hash = "sha256:ac913f8403b36a2c8610bbfd25b8013488533e71e62b4b4adce9c86c8cea905b", size = 44064, upload-time = "2025-10-06T05:36:04.368Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/c2c9ab44e181f043a86f9a8f84d5124b62dbcb3a02c0977ec72b9ac1d3e0/frozenlist-1.8.0-cp311-cp311-win_arm64.whl", hash = "sha256:d4d3214a0f8394edfa3e303136d0575eece0745ff2b47bd2cb2e66dd92d4351a", size = 39937, upload-time = "2025-10-06T05:36:05.669Z" }, + { url = "https://files.pythonhosted.org/packages/69/29/948b9aa87e75820a38650af445d2ef2b6b8a6fab1a23b6bb9e4ef0be2d59/frozenlist-1.8.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:78f7b9e5d6f2fdb88cdde9440dc147259b62b9d3b019924def9f6478be254ac1", size = 87782, upload-time = "2025-10-06T05:36:06.649Z" }, + { url = "https://files.pythonhosted.org/packages/64/80/4f6e318ee2a7c0750ed724fa33a4bdf1eacdc5a39a7a24e818a773cd91af/frozenlist-1.8.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:229bf37d2e4acdaf808fd3f06e854a4a7a3661e871b10dc1f8f1896a3b05f18b", size = 50594, upload-time = "2025-10-06T05:36:07.69Z" }, + { url = "https://files.pythonhosted.org/packages/2b/94/5c8a2b50a496b11dd519f4a24cb5496cf125681dd99e94c604ccdea9419a/frozenlist-1.8.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f833670942247a14eafbb675458b4e61c82e002a148f49e68257b79296e865c4", size = 50448, upload-time = "2025-10-06T05:36:08.78Z" }, + { url = "https://files.pythonhosted.org/packages/6a/bd/d91c5e39f490a49df14320f4e8c80161cfcce09f1e2cde1edd16a551abb3/frozenlist-1.8.0-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:494a5952b1c597ba44e0e78113a7266e656b9794eec897b19ead706bd7074383", size = 242411, upload-time = "2025-10-06T05:36:09.801Z" }, + { url = "https://files.pythonhosted.org/packages/8f/83/f61505a05109ef3293dfb1ff594d13d64a2324ac3482be2cedc2be818256/frozenlist-1.8.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:96f423a119f4777a4a056b66ce11527366a8bb92f54e541ade21f2374433f6d4", size = 243014, upload-time = "2025-10-06T05:36:11.394Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cb/cb6c7b0f7d4023ddda30cf56b8b17494eb3a79e3fda666bf735f63118b35/frozenlist-1.8.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3462dd9475af2025c31cc61be6652dfa25cbfb56cbbf52f4ccfe029f38decaf8", size = 234909, upload-time = "2025-10-06T05:36:12.598Z" }, + { url = "https://files.pythonhosted.org/packages/31/c5/cd7a1f3b8b34af009fb17d4123c5a778b44ae2804e3ad6b86204255f9ec5/frozenlist-1.8.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4c800524c9cd9bac5166cd6f55285957fcfc907db323e193f2afcd4d9abd69b", size = 250049, upload-time = "2025-10-06T05:36:14.065Z" }, + { url = "https://files.pythonhosted.org/packages/c0/01/2f95d3b416c584a1e7f0e1d6d31998c4a795f7544069ee2e0962a4b60740/frozenlist-1.8.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d6a5df73acd3399d893dafc71663ad22534b5aa4f94e8a2fabfe856c3c1b6a52", size = 256485, upload-time = "2025-10-06T05:36:15.39Z" }, + { url = "https://files.pythonhosted.org/packages/ce/03/024bf7720b3abaebcff6d0793d73c154237b85bdf67b7ed55e5e9596dc9a/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:405e8fe955c2280ce66428b3ca55e12b3c4e9c336fb2103a4937e891c69a4a29", size = 237619, upload-time = "2025-10-06T05:36:16.558Z" }, + { url = "https://files.pythonhosted.org/packages/69/fa/f8abdfe7d76b731f5d8bd217827cf6764d4f1d9763407e42717b4bed50a0/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:908bd3f6439f2fef9e85031b59fd4f1297af54415fb60e4254a95f75b3cab3f3", size = 250320, upload-time = "2025-10-06T05:36:17.821Z" }, + { url = "https://files.pythonhosted.org/packages/f5/3c/b051329f718b463b22613e269ad72138cc256c540f78a6de89452803a47d/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:294e487f9ec720bd8ffcebc99d575f7eff3568a08a253d1ee1a0378754b74143", size = 246820, upload-time = "2025-10-06T05:36:19.046Z" }, + { url = "https://files.pythonhosted.org/packages/0f/ae/58282e8f98e444b3f4dd42448ff36fa38bef29e40d40f330b22e7108f565/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:74c51543498289c0c43656701be6b077f4b265868fa7f8a8859c197006efb608", size = 250518, upload-time = "2025-10-06T05:36:20.763Z" }, + { url = "https://files.pythonhosted.org/packages/8f/96/007e5944694d66123183845a106547a15944fbbb7154788cbf7272789536/frozenlist-1.8.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:776f352e8329135506a1d6bf16ac3f87bc25b28e765949282dcc627af36123aa", size = 239096, upload-time = "2025-10-06T05:36:22.129Z" }, + { url = "https://files.pythonhosted.org/packages/66/bb/852b9d6db2fa40be96f29c0d1205c306288f0684df8fd26ca1951d461a56/frozenlist-1.8.0-cp312-cp312-win32.whl", hash = "sha256:433403ae80709741ce34038da08511d4a77062aa924baf411ef73d1146e74faf", size = 39985, upload-time = "2025-10-06T05:36:23.661Z" }, + { url = "https://files.pythonhosted.org/packages/b8/af/38e51a553dd66eb064cdf193841f16f077585d4d28394c2fa6235cb41765/frozenlist-1.8.0-cp312-cp312-win_amd64.whl", hash = "sha256:34187385b08f866104f0c0617404c8eb08165ab1272e884abc89c112e9c00746", size = 44591, upload-time = "2025-10-06T05:36:24.958Z" }, + { url = "https://files.pythonhosted.org/packages/a7/06/1dc65480ab147339fecc70797e9c2f69d9cea9cf38934ce08df070fdb9cb/frozenlist-1.8.0-cp312-cp312-win_arm64.whl", hash = "sha256:fe3c58d2f5db5fbd18c2987cba06d51b0529f52bc3a6cdc33d3f4eab725104bd", size = 40102, upload-time = "2025-10-06T05:36:26.333Z" }, + { url = "https://files.pythonhosted.org/packages/2d/40/0832c31a37d60f60ed79e9dfb5a92e1e2af4f40a16a29abcc7992af9edff/frozenlist-1.8.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8d92f1a84bb12d9e56f818b3a746f3efba93c1b63c8387a73dde655e1e42282a", size = 85717, upload-time = "2025-10-06T05:36:27.341Z" }, + { url = "https://files.pythonhosted.org/packages/30/ba/b0b3de23f40bc55a7057bd38434e25c34fa48e17f20ee273bbde5e0650f3/frozenlist-1.8.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:96153e77a591c8adc2ee805756c61f59fef4cf4073a9275ee86fe8cba41241f7", size = 49651, upload-time = "2025-10-06T05:36:28.855Z" }, + { url = "https://files.pythonhosted.org/packages/0c/ab/6e5080ee374f875296c4243c381bbdef97a9ac39c6e3ce1d5f7d42cb78d6/frozenlist-1.8.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f21f00a91358803399890ab167098c131ec2ddd5f8f5fd5fe9c9f2c6fcd91e40", size = 49417, upload-time = "2025-10-06T05:36:29.877Z" }, + { url = "https://files.pythonhosted.org/packages/d5/4e/e4691508f9477ce67da2015d8c00acd751e6287739123113a9fca6f1604e/frozenlist-1.8.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:fb30f9626572a76dfe4293c7194a09fb1fe93ba94c7d4f720dfae3b646b45027", size = 234391, upload-time = "2025-10-06T05:36:31.301Z" }, + { url = "https://files.pythonhosted.org/packages/40/76/c202df58e3acdf12969a7895fd6f3bc016c642e6726aa63bd3025e0fc71c/frozenlist-1.8.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eaa352d7047a31d87dafcacbabe89df0aa506abb5b1b85a2fb91bc3faa02d822", size = 233048, upload-time = "2025-10-06T05:36:32.531Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c0/8746afb90f17b73ca5979c7a3958116e105ff796e718575175319b5bb4ce/frozenlist-1.8.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:03ae967b4e297f58f8c774c7eabcce57fe3c2434817d4385c50661845a058121", size = 226549, upload-time = "2025-10-06T05:36:33.706Z" }, + { url = "https://files.pythonhosted.org/packages/7e/eb/4c7eefc718ff72f9b6c4893291abaae5fbc0c82226a32dcd8ef4f7a5dbef/frozenlist-1.8.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f6292f1de555ffcc675941d65fffffb0a5bcd992905015f85d0592201793e0e5", size = 239833, upload-time = "2025-10-06T05:36:34.947Z" }, + { url = "https://files.pythonhosted.org/packages/c2/4e/e5c02187cf704224f8b21bee886f3d713ca379535f16893233b9d672ea71/frozenlist-1.8.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:29548f9b5b5e3460ce7378144c3010363d8035cea44bc0bf02d57f5a685e084e", size = 245363, upload-time = "2025-10-06T05:36:36.534Z" }, + { url = "https://files.pythonhosted.org/packages/1f/96/cb85ec608464472e82ad37a17f844889c36100eed57bea094518bf270692/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:ec3cc8c5d4084591b4237c0a272cc4f50a5b03396a47d9caaf76f5d7b38a4f11", size = 229314, upload-time = "2025-10-06T05:36:38.582Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6f/4ae69c550e4cee66b57887daeebe006fe985917c01d0fff9caab9883f6d0/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:517279f58009d0b1f2e7c1b130b377a349405da3f7621ed6bfae50b10adf20c1", size = 243365, upload-time = "2025-10-06T05:36:40.152Z" }, + { url = "https://files.pythonhosted.org/packages/7a/58/afd56de246cf11780a40a2c28dc7cbabbf06337cc8ddb1c780a2d97e88d8/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:db1e72ede2d0d7ccb213f218df6a078a9c09a7de257c2fe8fcef16d5925230b1", size = 237763, upload-time = "2025-10-06T05:36:41.355Z" }, + { url = "https://files.pythonhosted.org/packages/cb/36/cdfaf6ed42e2644740d4a10452d8e97fa1c062e2a8006e4b09f1b5fd7d63/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:b4dec9482a65c54a5044486847b8a66bf10c9cb4926d42927ec4e8fd5db7fed8", size = 240110, upload-time = "2025-10-06T05:36:42.716Z" }, + { url = "https://files.pythonhosted.org/packages/03/a8/9ea226fbefad669f11b52e864c55f0bd57d3c8d7eb07e9f2e9a0b39502e1/frozenlist-1.8.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:21900c48ae04d13d416f0e1e0c4d81f7931f73a9dfa0b7a8746fb2fe7dd970ed", size = 233717, upload-time = "2025-10-06T05:36:44.251Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0b/1b5531611e83ba7d13ccc9988967ea1b51186af64c42b7a7af465dcc9568/frozenlist-1.8.0-cp313-cp313-win32.whl", hash = "sha256:8b7b94a067d1c504ee0b16def57ad5738701e4ba10cec90529f13fa03c833496", size = 39628, upload-time = "2025-10-06T05:36:45.423Z" }, + { url = "https://files.pythonhosted.org/packages/d8/cf/174c91dbc9cc49bc7b7aab74d8b734e974d1faa8f191c74af9b7e80848e6/frozenlist-1.8.0-cp313-cp313-win_amd64.whl", hash = "sha256:878be833caa6a3821caf85eb39c5ba92d28e85df26d57afb06b35b2efd937231", size = 43882, upload-time = "2025-10-06T05:36:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/c1/17/502cd212cbfa96eb1388614fe39a3fc9ab87dbbe042b66f97acb57474834/frozenlist-1.8.0-cp313-cp313-win_arm64.whl", hash = "sha256:44389d135b3ff43ba8cc89ff7f51f5a0bb6b63d829c8300f79a2fe4fe61bcc62", size = 39676, upload-time = "2025-10-06T05:36:47.8Z" }, + { url = "https://files.pythonhosted.org/packages/d2/5c/3bbfaa920dfab09e76946a5d2833a7cbdf7b9b4a91c714666ac4855b88b4/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:e25ac20a2ef37e91c1b39938b591457666a0fa835c7783c3a8f33ea42870db94", size = 89235, upload-time = "2025-10-06T05:36:48.78Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d6/f03961ef72166cec1687e84e8925838442b615bd0b8854b54923ce5b7b8a/frozenlist-1.8.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:07cdca25a91a4386d2e76ad992916a85038a9b97561bf7a3fd12d5d9ce31870c", size = 50742, upload-time = "2025-10-06T05:36:49.837Z" }, + { url = "https://files.pythonhosted.org/packages/1e/bb/a6d12b7ba4c3337667d0e421f7181c82dda448ce4e7ad7ecd249a16fa806/frozenlist-1.8.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:4e0c11f2cc6717e0a741f84a527c52616140741cd812a50422f83dc31749fb52", size = 51725, upload-time = "2025-10-06T05:36:50.851Z" }, + { url = "https://files.pythonhosted.org/packages/bc/71/d1fed0ffe2c2ccd70b43714c6cab0f4188f09f8a67a7914a6b46ee30f274/frozenlist-1.8.0-cp313-cp313t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3210649ee28062ea6099cfda39e147fa1bc039583c8ee4481cb7811e2448c51", size = 284533, upload-time = "2025-10-06T05:36:51.898Z" }, + { url = "https://files.pythonhosted.org/packages/c9/1f/fb1685a7b009d89f9bf78a42d94461bc06581f6e718c39344754a5d9bada/frozenlist-1.8.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:581ef5194c48035a7de2aefc72ac6539823bb71508189e5de01d60c9dcd5fa65", size = 292506, upload-time = "2025-10-06T05:36:53.101Z" }, + { url = "https://files.pythonhosted.org/packages/e6/3b/b991fe1612703f7e0d05c0cf734c1b77aaf7c7d321df4572e8d36e7048c8/frozenlist-1.8.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3ef2d026f16a2b1866e1d86fc4e1291e1ed8a387b2c333809419a2f8b3a77b82", size = 274161, upload-time = "2025-10-06T05:36:54.309Z" }, + { url = "https://files.pythonhosted.org/packages/ca/ec/c5c618767bcdf66e88945ec0157d7f6c4a1322f1473392319b7a2501ded7/frozenlist-1.8.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5500ef82073f599ac84d888e3a8c1f77ac831183244bfd7f11eaa0289fb30714", size = 294676, upload-time = "2025-10-06T05:36:55.566Z" }, + { url = "https://files.pythonhosted.org/packages/7c/ce/3934758637d8f8a88d11f0585d6495ef54b2044ed6ec84492a91fa3b27aa/frozenlist-1.8.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50066c3997d0091c411a66e710f4e11752251e6d2d73d70d8d5d4c76442a199d", size = 300638, upload-time = "2025-10-06T05:36:56.758Z" }, + { url = "https://files.pythonhosted.org/packages/fc/4f/a7e4d0d467298f42de4b41cbc7ddaf19d3cfeabaf9ff97c20c6c7ee409f9/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:5c1c8e78426e59b3f8005e9b19f6ff46e5845895adbde20ece9218319eca6506", size = 283067, upload-time = "2025-10-06T05:36:57.965Z" }, + { url = "https://files.pythonhosted.org/packages/dc/48/c7b163063d55a83772b268e6d1affb960771b0e203b632cfe09522d67ea5/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:eefdba20de0d938cec6a89bd4d70f346a03108a19b9df4248d3cf0d88f1b0f51", size = 292101, upload-time = "2025-10-06T05:36:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/9f/d0/2366d3c4ecdc2fd391e0afa6e11500bfba0ea772764d631bbf82f0136c9d/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cf253e0e1c3ceb4aaff6df637ce033ff6535fb8c70a764a8f46aafd3d6ab798e", size = 289901, upload-time = "2025-10-06T05:37:00.811Z" }, + { url = "https://files.pythonhosted.org/packages/b8/94/daff920e82c1b70e3618a2ac39fbc01ae3e2ff6124e80739ce5d71c9b920/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:032efa2674356903cd0261c4317a561a6850f3ac864a63fc1583147fb05a79b0", size = 289395, upload-time = "2025-10-06T05:37:02.115Z" }, + { url = "https://files.pythonhosted.org/packages/e3/20/bba307ab4235a09fdcd3cc5508dbabd17c4634a1af4b96e0f69bfe551ebd/frozenlist-1.8.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6da155091429aeba16851ecb10a9104a108bcd32f6c1642867eadaee401c1c41", size = 283659, upload-time = "2025-10-06T05:37:03.711Z" }, + { url = "https://files.pythonhosted.org/packages/fd/00/04ca1c3a7a124b6de4f8a9a17cc2fcad138b4608e7a3fc5877804b8715d7/frozenlist-1.8.0-cp313-cp313t-win32.whl", hash = "sha256:0f96534f8bfebc1a394209427d0f8a63d343c9779cda6fc25e8e121b5fd8555b", size = 43492, upload-time = "2025-10-06T05:37:04.915Z" }, + { url = "https://files.pythonhosted.org/packages/59/5e/c69f733a86a94ab10f68e496dc6b7e8bc078ebb415281d5698313e3af3a1/frozenlist-1.8.0-cp313-cp313t-win_amd64.whl", hash = "sha256:5d63a068f978fc69421fb0e6eb91a9603187527c86b7cd3f534a5b77a592b888", size = 48034, upload-time = "2025-10-06T05:37:06.343Z" }, + { url = "https://files.pythonhosted.org/packages/16/6c/be9d79775d8abe79b05fa6d23da99ad6e7763a1d080fbae7290b286093fd/frozenlist-1.8.0-cp313-cp313t-win_arm64.whl", hash = "sha256:bf0a7e10b077bf5fb9380ad3ae8ce20ef919a6ad93b4552896419ac7e1d8e042", size = 41749, upload-time = "2025-10-06T05:37:07.431Z" }, + { url = "https://files.pythonhosted.org/packages/f1/c8/85da824b7e7b9b6e7f7705b2ecaf9591ba6f79c1177f324c2735e41d36a2/frozenlist-1.8.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:cee686f1f4cadeb2136007ddedd0aaf928ab95216e7691c63e50a8ec066336d0", size = 86127, upload-time = "2025-10-06T05:37:08.438Z" }, + { url = "https://files.pythonhosted.org/packages/8e/e8/a1185e236ec66c20afd72399522f142c3724c785789255202d27ae992818/frozenlist-1.8.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:119fb2a1bd47307e899c2fac7f28e85b9a543864df47aa7ec9d3c1b4545f096f", size = 49698, upload-time = "2025-10-06T05:37:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/a1/93/72b1736d68f03fda5fdf0f2180fb6caaae3894f1b854d006ac61ecc727ee/frozenlist-1.8.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4970ece02dbc8c3a92fcc5228e36a3e933a01a999f7094ff7c23fbd2beeaa67c", size = 49749, upload-time = "2025-10-06T05:37:10.569Z" }, + { url = "https://files.pythonhosted.org/packages/a7/b2/fabede9fafd976b991e9f1b9c8c873ed86f202889b864756f240ce6dd855/frozenlist-1.8.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:cba69cb73723c3f329622e34bdbf5ce1f80c21c290ff04256cff1cd3c2036ed2", size = 231298, upload-time = "2025-10-06T05:37:11.993Z" }, + { url = "https://files.pythonhosted.org/packages/3a/3b/d9b1e0b0eed36e70477ffb8360c49c85c8ca8ef9700a4e6711f39a6e8b45/frozenlist-1.8.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:778a11b15673f6f1df23d9586f83c4846c471a8af693a22e066508b77d201ec8", size = 232015, upload-time = "2025-10-06T05:37:13.194Z" }, + { url = "https://files.pythonhosted.org/packages/dc/94/be719d2766c1138148564a3960fc2c06eb688da592bdc25adcf856101be7/frozenlist-1.8.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0325024fe97f94c41c08872db482cf8ac4800d80e79222c6b0b7b162d5b13686", size = 225038, upload-time = "2025-10-06T05:37:14.577Z" }, + { url = "https://files.pythonhosted.org/packages/e4/09/6712b6c5465f083f52f50cf74167b92d4ea2f50e46a9eea0523d658454ae/frozenlist-1.8.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:97260ff46b207a82a7567b581ab4190bd4dfa09f4db8a8b49d1a958f6aa4940e", size = 240130, upload-time = "2025-10-06T05:37:15.781Z" }, + { url = "https://files.pythonhosted.org/packages/f8/d4/cd065cdcf21550b54f3ce6a22e143ac9e4836ca42a0de1022da8498eac89/frozenlist-1.8.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:54b2077180eb7f83dd52c40b2750d0a9f175e06a42e3213ce047219de902717a", size = 242845, upload-time = "2025-10-06T05:37:17.037Z" }, + { url = "https://files.pythonhosted.org/packages/62/c3/f57a5c8c70cd1ead3d5d5f776f89d33110b1addae0ab010ad774d9a44fb9/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:2f05983daecab868a31e1da44462873306d3cbfd76d1f0b5b69c473d21dbb128", size = 229131, upload-time = "2025-10-06T05:37:18.221Z" }, + { url = "https://files.pythonhosted.org/packages/6c/52/232476fe9cb64f0742f3fde2b7d26c1dac18b6d62071c74d4ded55e0ef94/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:33f48f51a446114bc5d251fb2954ab0164d5be02ad3382abcbfe07e2531d650f", size = 240542, upload-time = "2025-10-06T05:37:19.771Z" }, + { url = "https://files.pythonhosted.org/packages/5f/85/07bf3f5d0fb5414aee5f47d33c6f5c77bfe49aac680bfece33d4fdf6a246/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:154e55ec0655291b5dd1b8731c637ecdb50975a2ae70c606d100750a540082f7", size = 237308, upload-time = "2025-10-06T05:37:20.969Z" }, + { url = "https://files.pythonhosted.org/packages/11/99/ae3a33d5befd41ac0ca2cc7fd3aa707c9c324de2e89db0e0f45db9a64c26/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:4314debad13beb564b708b4a496020e5306c7333fa9a3ab90374169a20ffab30", size = 238210, upload-time = "2025-10-06T05:37:22.252Z" }, + { url = "https://files.pythonhosted.org/packages/b2/60/b1d2da22f4970e7a155f0adde9b1435712ece01b3cd45ba63702aea33938/frozenlist-1.8.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:073f8bf8becba60aa931eb3bc420b217bb7d5b8f4750e6f8b3be7f3da85d38b7", size = 231972, upload-time = "2025-10-06T05:37:23.5Z" }, + { url = "https://files.pythonhosted.org/packages/3f/ab/945b2f32de889993b9c9133216c068b7fcf257d8595a0ac420ac8677cab0/frozenlist-1.8.0-cp314-cp314-win32.whl", hash = "sha256:bac9c42ba2ac65ddc115d930c78d24ab8d4f465fd3fc473cdedfccadb9429806", size = 40536, upload-time = "2025-10-06T05:37:25.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/ad/9caa9b9c836d9ad6f067157a531ac48b7d36499f5036d4141ce78c230b1b/frozenlist-1.8.0-cp314-cp314-win_amd64.whl", hash = "sha256:3e0761f4d1a44f1d1a47996511752cf3dcec5bbdd9cc2b4fe595caf97754b7a0", size = 44330, upload-time = "2025-10-06T05:37:26.928Z" }, + { url = "https://files.pythonhosted.org/packages/82/13/e6950121764f2676f43534c555249f57030150260aee9dcf7d64efda11dd/frozenlist-1.8.0-cp314-cp314-win_arm64.whl", hash = "sha256:d1eaff1d00c7751b7c6662e9c5ba6eb2c17a2306ba5e2a37f24ddf3cc953402b", size = 40627, upload-time = "2025-10-06T05:37:28.075Z" }, + { url = "https://files.pythonhosted.org/packages/c0/c7/43200656ecc4e02d3f8bc248df68256cd9572b3f0017f0a0c4e93440ae23/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d3bb933317c52d7ea5004a1c442eef86f426886fba134ef8cf4226ea6ee1821d", size = 89238, upload-time = "2025-10-06T05:37:29.373Z" }, + { url = "https://files.pythonhosted.org/packages/d1/29/55c5f0689b9c0fb765055629f472c0de484dcaf0acee2f7707266ae3583c/frozenlist-1.8.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:8009897cdef112072f93a0efdce29cd819e717fd2f649ee3016efd3cd885a7ed", size = 50738, upload-time = "2025-10-06T05:37:30.792Z" }, + { url = "https://files.pythonhosted.org/packages/ba/7d/b7282a445956506fa11da8c2db7d276adcbf2b17d8bb8407a47685263f90/frozenlist-1.8.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2c5dcbbc55383e5883246d11fd179782a9d07a986c40f49abe89ddf865913930", size = 51739, upload-time = "2025-10-06T05:37:32.127Z" }, + { url = "https://files.pythonhosted.org/packages/62/1c/3d8622e60d0b767a5510d1d3cf21065b9db874696a51ea6d7a43180a259c/frozenlist-1.8.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:39ecbc32f1390387d2aa4f5a995e465e9e2f79ba3adcac92d68e3e0afae6657c", size = 284186, upload-time = "2025-10-06T05:37:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/2d/14/aa36d5f85a89679a85a1d44cd7a6657e0b1c75f61e7cad987b203d2daca8/frozenlist-1.8.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92db2bf818d5cc8d9c1f1fc56b897662e24ea5adb36ad1f1d82875bd64e03c24", size = 292196, upload-time = "2025-10-06T05:37:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/05/23/6bde59eb55abd407d34f77d39a5126fb7b4f109a3f611d3929f14b700c66/frozenlist-1.8.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2dc43a022e555de94c3b68a4ef0b11c4f747d12c024a520c7101709a2144fb37", size = 273830, upload-time = "2025-10-06T05:37:37.663Z" }, + { url = "https://files.pythonhosted.org/packages/d2/3f/22cff331bfad7a8afa616289000ba793347fcd7bc275f3b28ecea2a27909/frozenlist-1.8.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb89a7f2de3602cfed448095bab3f178399646ab7c61454315089787df07733a", size = 294289, upload-time = "2025-10-06T05:37:39.261Z" }, + { url = "https://files.pythonhosted.org/packages/a4/89/5b057c799de4838b6c69aa82b79705f2027615e01be996d2486a69ca99c4/frozenlist-1.8.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:33139dc858c580ea50e7e60a1b0ea003efa1fd42e6ec7fdbad78fff65fad2fd2", size = 300318, upload-time = "2025-10-06T05:37:43.213Z" }, + { url = "https://files.pythonhosted.org/packages/30/de/2c22ab3eb2a8af6d69dc799e48455813bab3690c760de58e1bf43b36da3e/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:168c0969a329b416119507ba30b9ea13688fafffac1b7822802537569a1cb0ef", size = 282814, upload-time = "2025-10-06T05:37:45.337Z" }, + { url = "https://files.pythonhosted.org/packages/59/f7/970141a6a8dbd7f556d94977858cfb36fa9b66e0892c6dd780d2219d8cd8/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:28bd570e8e189d7f7b001966435f9dac6718324b5be2990ac496cf1ea9ddb7fe", size = 291762, upload-time = "2025-10-06T05:37:46.657Z" }, + { url = "https://files.pythonhosted.org/packages/c1/15/ca1adae83a719f82df9116d66f5bb28bb95557b3951903d39135620ef157/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b2a095d45c5d46e5e79ba1e5b9cb787f541a8dee0433836cea4b96a2c439dcd8", size = 289470, upload-time = "2025-10-06T05:37:47.946Z" }, + { url = "https://files.pythonhosted.org/packages/ac/83/dca6dc53bf657d371fbc88ddeb21b79891e747189c5de990b9dfff2ccba1/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:eab8145831a0d56ec9c4139b6c3e594c7a83c2c8be25d5bcf2d86136a532287a", size = 289042, upload-time = "2025-10-06T05:37:49.499Z" }, + { url = "https://files.pythonhosted.org/packages/96/52/abddd34ca99be142f354398700536c5bd315880ed0a213812bc491cff5e4/frozenlist-1.8.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:974b28cf63cc99dfb2188d8d222bc6843656188164848c4f679e63dae4b0708e", size = 283148, upload-time = "2025-10-06T05:37:50.745Z" }, + { url = "https://files.pythonhosted.org/packages/af/d3/76bd4ed4317e7119c2b7f57c3f6934aba26d277acc6309f873341640e21f/frozenlist-1.8.0-cp314-cp314t-win32.whl", hash = "sha256:342c97bf697ac5480c0a7ec73cd700ecfa5a8a40ac923bd035484616efecc2df", size = 44676, upload-time = "2025-10-06T05:37:52.222Z" }, + { url = "https://files.pythonhosted.org/packages/89/76/c615883b7b521ead2944bb3480398cbb07e12b7b4e4d073d3752eb721558/frozenlist-1.8.0-cp314-cp314t-win_amd64.whl", hash = "sha256:06be8f67f39c8b1dc671f5d83aaefd3358ae5cdcf8314552c57e7ed3e6475bdd", size = 49451, upload-time = "2025-10-06T05:37:53.425Z" }, + { url = "https://files.pythonhosted.org/packages/e0/a3/5982da14e113d07b325230f95060e2169f5311b1017ea8af2a29b374c289/frozenlist-1.8.0-cp314-cp314t-win_arm64.whl", hash = "sha256:102e6314ca4da683dca92e3b1355490fed5f313b768500084fbe6371fddfdb79", size = 42507, upload-time = "2025-10-06T05:37:54.513Z" }, + { url = "https://files.pythonhosted.org/packages/c2/59/ae5cdac87a00962122ea37bb346d41b66aec05f9ce328fa2b9e216f8967b/frozenlist-1.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:d8b7138e5cd0647e4523d6685b0eac5d4be9a184ae9634492f25c6eb38c12a47", size = 86967, upload-time = "2025-10-06T05:37:55.607Z" }, + { url = "https://files.pythonhosted.org/packages/8a/10/17059b2db5a032fd9323c41c39e9d1f5f9d0c8f04d1e4e3e788573086e61/frozenlist-1.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a6483e309ca809f1efd154b4d37dc6d9f61037d6c6a81c2dc7a15cb22c8c5dca", size = 49984, upload-time = "2025-10-06T05:37:57.049Z" }, + { url = "https://files.pythonhosted.org/packages/4b/de/ad9d82ca8e5fa8f0c636e64606553c79e2b859ad253030b62a21fe9986f5/frozenlist-1.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1b9290cf81e95e93fdf90548ce9d3c1211cf574b8e3f4b3b7cb0537cf2227068", size = 50240, upload-time = "2025-10-06T05:37:58.145Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/3dfb7767c2a67d123650122b62ce13c731b6c745bc14424eea67678b508c/frozenlist-1.8.0-cp39-cp39-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59a6a5876ca59d1b63af8cd5e7ffffb024c3dc1e9cf9301b21a2e76286505c95", size = 219472, upload-time = "2025-10-06T05:37:59.239Z" }, + { url = "https://files.pythonhosted.org/packages/0b/bf/5bf23d913a741b960d5c1dac7c1985d8a2a1d015772b2d18ea168b08e7ff/frozenlist-1.8.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6dc4126390929823e2d2d9dc79ab4046ed74680360fc5f38b585c12c66cdf459", size = 221531, upload-time = "2025-10-06T05:38:00.521Z" }, + { url = "https://files.pythonhosted.org/packages/d0/03/27ec393f3b55860859f4b74cdc8c2a4af3dbf3533305e8eacf48a4fd9a54/frozenlist-1.8.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:332db6b2563333c5671fecacd085141b5800cb866be16d5e3eb15a2086476675", size = 219211, upload-time = "2025-10-06T05:38:01.842Z" }, + { url = "https://files.pythonhosted.org/packages/3a/ad/0fd00c404fa73fe9b169429e9a972d5ed807973c40ab6b3cf9365a33d360/frozenlist-1.8.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9ff15928d62a0b80bb875655c39bf517938c7d589554cbd2669be42d97c2cb61", size = 231775, upload-time = "2025-10-06T05:38:03.384Z" }, + { url = "https://files.pythonhosted.org/packages/8a/c3/86962566154cb4d2995358bc8331bfc4ea19d07db1a96f64935a1607f2b6/frozenlist-1.8.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7bf6cdf8e07c8151fba6fe85735441240ec7f619f935a5205953d58009aef8c6", size = 236631, upload-time = "2025-10-06T05:38:04.609Z" }, + { url = "https://files.pythonhosted.org/packages/ea/9e/6ffad161dbd83782d2c66dc4d378a9103b31770cb1e67febf43aea42d202/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:48e6d3f4ec5c7273dfe83ff27c91083c6c9065af655dc2684d2c200c94308bb5", size = 218632, upload-time = "2025-10-06T05:38:05.917Z" }, + { url = "https://files.pythonhosted.org/packages/58/b2/4677eee46e0a97f9b30735e6ad0bf6aba3e497986066eb68807ac85cf60f/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:1a7607e17ad33361677adcd1443edf6f5da0ce5e5377b798fba20fae194825f3", size = 235967, upload-time = "2025-10-06T05:38:07.614Z" }, + { url = "https://files.pythonhosted.org/packages/05/f3/86e75f8639c5a93745ca7addbbc9de6af56aebb930d233512b17e46f6493/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:5a3a935c3a4e89c733303a2d5a7c257ea44af3a56c8202df486b7f5de40f37e1", size = 228799, upload-time = "2025-10-06T05:38:08.845Z" }, + { url = "https://files.pythonhosted.org/packages/30/00/39aad3a7f0d98f5eb1d99a3c311215674ed87061aecee7851974b335c050/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:940d4a017dbfed9daf46a3b086e1d2167e7012ee297fef9e1c545c4d022f5178", size = 230566, upload-time = "2025-10-06T05:38:10.52Z" }, + { url = "https://files.pythonhosted.org/packages/0d/4d/aa144cac44568d137846ddc4d5210fb5d9719eb1d7ec6fa2728a54b5b94a/frozenlist-1.8.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b9be22a69a014bc47e78072d0ecae716f5eb56c15238acca0f43d6eb8e4a5bda", size = 217715, upload-time = "2025-10-06T05:38:11.832Z" }, + { url = "https://files.pythonhosted.org/packages/64/4c/8f665921667509d25a0dd72540513bc86b356c95541686f6442a3283019f/frozenlist-1.8.0-cp39-cp39-win32.whl", hash = "sha256:1aa77cb5697069af47472e39612976ed05343ff2e84a3dcf15437b232cbfd087", size = 39933, upload-time = "2025-10-06T05:38:13.061Z" }, + { url = "https://files.pythonhosted.org/packages/79/bd/bcc926f87027fad5e59926ff12d136e1082a115025d33c032d1cd69ab377/frozenlist-1.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:7398c222d1d405e796970320036b1b563892b65809d9e5261487bb2c7f7b5c6a", size = 44121, upload-time = "2025-10-06T05:38:14.572Z" }, + { url = "https://files.pythonhosted.org/packages/4c/07/9c2e4eb7584af4b705237b971b89a4155a8e57599c4483a131a39256a9a0/frozenlist-1.8.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f3b365f31c6cd4af24545ca0a244a53688cad8834e32f56831c4923b50a103", size = 40312, upload-time = "2025-10-06T05:38:15.699Z" }, + { url = "https://files.pythonhosted.org/packages/9a/9a/e35b4a917281c0b8419d4207f4334c8e8c5dbf4f3f5f9ada73958d937dcc/frozenlist-1.8.0-py3-none-any.whl", hash = "sha256:0c18a16eab41e82c295618a77502e17b195883241c563b00f0aa5106fc4eaa0d", size = 13409, upload-time = "2025-10-06T05:38:16.721Z" }, +] + +[[package]] +name = "humanize" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/66/a3921783d54be8a6870ac4ccffcd15c4dc0dd7fcce51c6d63b8c63935276/humanize-4.15.0.tar.gz", hash = "sha256:1dd098483eb1c7ee8e32eb2e99ad1910baefa4b75c3aff3a82f4d78688993b10", size = 83599, upload-time = "2025-12-20T20:16:13.19Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c5/7b/bca5613a0c3b542420cf92bd5e5fb8ebd5435ce1011a091f66bb7693285e/humanize-4.15.0-py3-none-any.whl", hash = "sha256:b1186eb9f5a9749cd9cb8565aee77919dd7c8d076161cf44d70e59e3301e1769", size = 132203, upload-time = "2025-12-20T20:16:11.67Z" }, +] + +[[package]] +name = "id" +version = "1.6.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/6d/04/c2156091427636080787aac190019dc64096e56a23b7364d3c1764ee3a06/id-1.6.1.tar.gz", hash = "sha256:d0732d624fb46fd4e7bc4e5152f00214450953b9e772c182c1c22964def1a069", size = 18088, upload-time = "2026-02-04T16:19:41.26Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/77/de194443bf38daed9452139e960c632b0ef9f9a5dd9ce605fdf18ca9f1b1/id-1.6.1-py3-none-any.whl", hash = "sha256:f5ec41ed2629a508f5d0988eda142e190c9c6da971100612c4de9ad9f9b237ca", size = 14689, upload-time = "2026-02-04T16:19:40.051Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "importlib-metadata" +version = "8.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", hash = "sha256:49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb", size = 57107, upload-time = "2025-12-21T10:00:19.278Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", hash = "sha256:5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151", size = 27865, upload-time = "2025-12-21T10:00:18.329Z" }, +] + +[[package]] +name = "importlib-resources" +version = "6.5.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "zipp", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/8c/f834fbf984f691b4f7ff60f50b514cc3de5cc08abfc3295564dd89c5e2e7/importlib_resources-6.5.2.tar.gz", hash = "sha256:185f87adef5bcc288449d98fb4fba07cea78bc036455dd44c5fc4a2fe78fed2c", size = 44693, upload-time = "2025-01-03T18:51:56.698Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/ed/1f1afb2e9e7f38a545d628f864d562a5ae64fe6f7a10e28ffb9b185b4e89/importlib_resources-6.5.2-py3-none-any.whl", hash = "sha256:789cfdc3ed28c78b67a06acb8126751ced69a3d5f79c095a98298cd8a760ccec", size = 37461, upload-time = "2025-01-03T18:51:54.306Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793, upload-time = "2025-03-19T20:09:59.721Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050, upload-time = "2025-03-19T20:10:01.071Z" }, +] + +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + +[[package]] +name = "ipython" +version = "8.18.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version < '3.10'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "jedi", marker = "python_full_version < '3.10'" }, + { name = "matplotlib-inline", marker = "python_full_version < '3.10'" }, + { name = "pexpect", marker = "python_full_version < '3.10' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version < '3.10'" }, + { name = "pygments", marker = "python_full_version < '3.10'" }, + { name = "stack-data", marker = "python_full_version < '3.10'" }, + { name = "traitlets", marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b1/b9/3ba6c45a6df813c09a48bac313c22ff83efa26cbb55011218d925a46e2ad/ipython-8.18.1.tar.gz", hash = "sha256:ca6f079bb33457c66e233e4580ebfc4128855b4cf6370dddd73842a9563e8a27", size = 5486330, upload-time = "2023-11-27T09:58:34.596Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/6b/d9fdcdef2eb6a23f391251fde8781c38d42acd82abe84d054cb74f7863b0/ipython-8.18.1-py3-none-any.whl", hash = "sha256:e8267419d72d81955ec1177f8a29aaa90ac80ad647499201119e2f05e99aa397", size = 808161, upload-time = "2023-11-27T09:58:30.538Z" }, +] + +[[package]] +name = "ipython" +version = "8.38.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version == '3.10.*'" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "jedi", marker = "python_full_version == '3.10.*'" }, + { name = "matplotlib-inline", marker = "python_full_version == '3.10.*'" }, + { name = "pexpect", marker = "python_full_version == '3.10.*' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version == '3.10.*'" }, + { name = "pygments", marker = "python_full_version == '3.10.*'" }, + { name = "stack-data", marker = "python_full_version == '3.10.*'" }, + { name = "traitlets", marker = "python_full_version == '3.10.*'" }, + { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e5/61/1810830e8b93c72dcd3c0f150c80a00c3deb229562d9423807ec92c3a539/ipython-8.38.0.tar.gz", hash = "sha256:9cfea8c903ce0867cc2f23199ed8545eb741f3a69420bfcf3743ad1cec856d39", size = 5513996, upload-time = "2026-01-05T10:59:06.901Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/df/db59624f4c71b39717c423409950ac3f2c8b2ce4b0aac843112c7fb3f721/ipython-8.38.0-py3-none-any.whl", hash = "sha256:750162629d800ac65bb3b543a14e7a74b0e88063eac9b92124d4b2aa3f6d8e86", size = 831813, upload-time = "2026-01-05T10:59:04.239Z" }, +] + +[[package]] +name = "ipython" +version = "9.10.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, + { name = "decorator", marker = "python_full_version >= '3.11'" }, + { name = "ipython-pygments-lexers", marker = "python_full_version >= '3.11'" }, + { name = "jedi", marker = "python_full_version >= '3.11'" }, + { name = "matplotlib-inline", marker = "python_full_version >= '3.11'" }, + { name = "pexpect", marker = "python_full_version >= '3.11' and sys_platform != 'emscripten' and sys_platform != 'win32'" }, + { name = "prompt-toolkit", marker = "python_full_version >= '3.11'" }, + { name = "pygments", marker = "python_full_version >= '3.11'" }, + { name = "stack-data", marker = "python_full_version >= '3.11'" }, + { name = "traitlets", marker = "python_full_version >= '3.11'" }, + { name = "typing-extensions", marker = "python_full_version == '3.11.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a6/60/2111715ea11f39b1535bed6024b7dec7918b71e5e5d30855a5b503056b50/ipython-9.10.0.tar.gz", hash = "sha256:cd9e656be97618a0676d058134cd44e6dc7012c0e5cb36a9ce96a8c904adaf77", size = 4426526, upload-time = "2026-02-02T10:00:33.594Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3d/aa/898dec789a05731cd5a9f50605b7b44a72bd198fd0d4528e11fc610177cc/ipython-9.10.0-py3-none-any.whl", hash = "sha256:c6ab68cc23bba8c7e18e9b932797014cc61ea7fd6f19de180ab9ba73e65ee58d", size = 622774, upload-time = "2026-02-02T10:00:31.503Z" }, +] + +[[package]] +name = "ipython-pygments-lexers" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pygments", marker = "python_full_version >= '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ef/4c/5dd1d8af08107f88c7f741ead7a40854b8ac24ddf9ae850afbcf698aa552/ipython_pygments_lexers-1.1.1.tar.gz", hash = "sha256:09c0138009e56b6854f9535736f4171d855c8c08a563a0dcd8022f78355c7e81", size = 8393, upload-time = "2025-01-17T11:24:34.505Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d9/33/1f075bf72b0b747cb3288d011319aaf64083cf2efef8354174e3ed4540e2/ipython_pygments_lexers-1.1.1-py3-none-any.whl", hash = "sha256:a9462224a505ade19a605f71f8fa63c2048833ce50abc86768a0d81d876dc81c", size = 8074, upload-time = "2025-01-17T11:24:33.271Z" }, +] + +[[package]] +name = "jaraco-classes" +version = "3.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/06/c0/ed4a27bc5571b99e3cff68f8a9fa5b56ff7df1c2251cc715a652ddd26402/jaraco.classes-3.4.0.tar.gz", hash = "sha256:47a024b51d0239c0dd8c8540c6c7f484be3b8fcf0b2d85c13825780d3b3f3acd", size = 11780, upload-time = "2024-03-31T07:27:36.643Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7f/66/b15ce62552d84bbfcec9a4873ab79d993a1dd4edb922cbfccae192bd5b5f/jaraco.classes-3.4.0-py3-none-any.whl", hash = "sha256:f662826b6bed8cace05e7ff873ce0f9283b5c924470fe664fff1c2f00f581790", size = 6777, upload-time = "2024-03-31T07:27:34.792Z" }, +] + +[[package]] +name = "jaraco-context" +version = "6.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "backports-tarfile", marker = "python_full_version < '3.12'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/cb/9c/a788f5bb29c61e456b8ee52ce76dbdd32fd72cd73dd67bc95f42c7a8d13c/jaraco_context-6.1.0.tar.gz", hash = "sha256:129a341b0a85a7db7879e22acd66902fda67882db771754574338898b2d5d86f", size = 15850, upload-time = "2026-01-13T02:53:53.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8d/48/aa685dbf1024c7bd82bede569e3a85f82c32fd3d79ba5fea578f0159571a/jaraco_context-6.1.0-py3-none-any.whl", hash = "sha256:a43b5ed85815223d0d3cfdb6d7ca0d2bc8946f28f30b6f3216bda070f68badda", size = 7065, upload-time = "2026-01-13T02:53:53.031Z" }, +] + +[[package]] +name = "jaraco-functools" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "more-itertools" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0f/27/056e0638a86749374d6f57d0b0db39f29509cce9313cf91bdc0ac4d91084/jaraco_functools-4.4.0.tar.gz", hash = "sha256:da21933b0417b89515562656547a77b4931f98176eb173644c0d35032a33d6bb", size = 19943, upload-time = "2025-12-21T09:29:43.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fd/c4/813bb09f0985cb21e959f21f2464169eca882656849adf727ac7bb7e1767/jaraco_functools-4.4.0-py3-none-any.whl", hash = "sha256:9eec1e36f45c818d9bf307c8948eb03b2b56cd44087b3cdc989abca1f20b9176", size = 10481, upload-time = "2025-12-21T09:29:42.27Z" }, +] + +[[package]] +name = "jedi" +version = "0.19.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "parso" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/72/3a/79a912fbd4d8dd6fbb02bf69afd3bb72cf0c729bb3063c6f4498603db17a/jedi-0.19.2.tar.gz", hash = "sha256:4770dc3de41bde3966b02eb84fbcf557fb33cce26ad23da12c742fb50ecb11f0", size = 1231287, upload-time = "2024-11-11T01:41:42.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/5a/9cac0c82afec3d09ccd97c8b6502d48f165f9124db81b4bcb90b4af974ee/jedi-0.19.2-py2.py3-none-any.whl", hash = "sha256:a8ef22bde8490f57fe5c7681a3c83cb58874daf72b4784de3cce5b6ef6edb5b9", size = 1572278, upload-time = "2024-11-11T01:41:40.175Z" }, +] + +[[package]] +name = "jeepney" +version = "0.9.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7b/6f/357efd7602486741aa73ffc0617fb310a29b588ed0fd69c2399acbb85b0c/jeepney-0.9.0.tar.gz", hash = "sha256:cf0e9e845622b81e4a28df94c40345400256ec608d0e55bb8a3feaa9163f5732", size = 106758, upload-time = "2025-02-27T18:51:01.684Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b2/a3/e137168c9c44d18eff0376253da9f1e9234d0239e0ee230d2fee6cea8e55/jeepney-0.9.0-py3-none-any.whl", hash = "sha256:97e5714520c16fc0a45695e5365a2e11b81ea79bba796e26f9f1d178cb182683", size = 49010, upload-time = "2025-02-27T18:51:00.104Z" }, +] + +[[package]] +name = "keyring" +version = "25.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.12'" }, + { name = "jaraco-classes" }, + { name = "jaraco-context" }, + { name = "jaraco-functools" }, + { name = "jeepney", marker = "sys_platform == 'linux'" }, + { name = "pywin32-ctypes", marker = "sys_platform == 'win32'" }, + { name = "secretstorage", version = "3.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and sys_platform == 'linux'" }, + { name = "secretstorage", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/43/4b/674af6ef2f97d56f0ab5153bf0bfa28ccb6c3ed4d1babf4305449668807b/keyring-25.7.0.tar.gz", hash = "sha256:fe01bd85eb3f8fb3dd0405defdeac9a5b4f6f0439edbb3149577f244a2e8245b", size = 63516, upload-time = "2025-11-16T16:26:09.482Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/db/e655086b7f3a705df045bf0933bdd9c2f79bb3c97bfef1384598bb79a217/keyring-25.7.0-py3-none-any.whl", hash = "sha256:be4a0b195f149690c166e850609a477c532ddbfbaed96a404d4e43f8d5e2689f", size = 39160, upload-time = "2025-11-16T16:26:08.402Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.7" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/85/4d/2255e1c76304cbd60b48cee302b66d1dde4468dc5b1160e4b7cb43778f2a/kiwisolver-1.4.7.tar.gz", hash = "sha256:9893ff81bd7107f7b685d3017cc6583daadb4fc26e4a888350df530e41980a60", size = 97286, upload-time = "2024-09-04T09:39:44.302Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/97/14/fc943dd65268a96347472b4fbe5dcc2f6f55034516f80576cd0dd3a8930f/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:8a9c83f75223d5e48b0bc9cb1bf2776cf01563e00ade8775ffe13b0b6e1af3a6", size = 122440, upload-time = "2024-09-04T09:03:44.9Z" }, + { url = "https://files.pythonhosted.org/packages/1e/46/e68fed66236b69dd02fcdb506218c05ac0e39745d696d22709498896875d/kiwisolver-1.4.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:58370b1ffbd35407444d57057b57da5d6549d2d854fa30249771775c63b5fe17", size = 65758, upload-time = "2024-09-04T09:03:46.582Z" }, + { url = "https://files.pythonhosted.org/packages/ef/fa/65de49c85838681fc9cb05de2a68067a683717321e01ddafb5b8024286f0/kiwisolver-1.4.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:aa0abdf853e09aff551db11fce173e2177d00786c688203f52c87ad7fcd91ef9", size = 64311, upload-time = "2024-09-04T09:03:47.973Z" }, + { url = "https://files.pythonhosted.org/packages/42/9c/cc8d90f6ef550f65443bad5872ffa68f3dee36de4974768628bea7c14979/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:8d53103597a252fb3ab8b5845af04c7a26d5e7ea8122303dd7a021176a87e8b9", size = 1637109, upload-time = "2024-09-04T09:03:49.281Z" }, + { url = "https://files.pythonhosted.org/packages/55/91/0a57ce324caf2ff5403edab71c508dd8f648094b18cfbb4c8cc0fde4a6ac/kiwisolver-1.4.7-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:88f17c5ffa8e9462fb79f62746428dd57b46eb931698e42e990ad63103f35e6c", size = 1617814, upload-time = "2024-09-04T09:03:51.444Z" }, + { url = "https://files.pythonhosted.org/packages/12/5d/c36140313f2510e20207708adf36ae4919416d697ee0236b0ddfb6fd1050/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:88a9ca9c710d598fd75ee5de59d5bda2684d9db36a9f50b6125eaea3969c2599", size = 1400881, upload-time = "2024-09-04T09:03:53.357Z" }, + { url = "https://files.pythonhosted.org/packages/56/d0/786e524f9ed648324a466ca8df86298780ef2b29c25313d9a4f16992d3cf/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f4d742cb7af1c28303a51b7a27aaee540e71bb8e24f68c736f6f2ffc82f2bf05", size = 1512972, upload-time = "2024-09-04T09:03:55.082Z" }, + { url = "https://files.pythonhosted.org/packages/67/5a/77851f2f201e6141d63c10a0708e996a1363efaf9e1609ad0441b343763b/kiwisolver-1.4.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e28c7fea2196bf4c2f8d46a0415c77a1c480cc0724722f23d7410ffe9842c407", size = 1444787, upload-time = "2024-09-04T09:03:56.588Z" }, + { url = "https://files.pythonhosted.org/packages/06/5f/1f5eaab84355885e224a6fc8d73089e8713dc7e91c121f00b9a1c58a2195/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:e968b84db54f9d42046cf154e02911e39c0435c9801681e3fc9ce8a3c4130278", size = 2199212, upload-time = "2024-09-04T09:03:58.557Z" }, + { url = "https://files.pythonhosted.org/packages/b5/28/9152a3bfe976a0ae21d445415defc9d1cd8614b2910b7614b30b27a47270/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:0c18ec74c0472de033e1bebb2911c3c310eef5649133dd0bedf2a169a1b269e5", size = 2346399, upload-time = "2024-09-04T09:04:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/26/f6/453d1904c52ac3b400f4d5e240ac5fec25263716723e44be65f4d7149d13/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8f0ea6da6d393d8b2e187e6a5e3fb81f5862010a40c3945e2c6d12ae45cfb2ad", size = 2308688, upload-time = "2024-09-04T09:04:02.216Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/d4968499441b9ae187e81745e3277a8b4d7c60840a52dc9d535a7909fac3/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:f106407dda69ae456dd1227966bf445b157ccc80ba0dff3802bb63f30b74e895", size = 2445493, upload-time = "2024-09-04T09:04:04.571Z" }, + { url = "https://files.pythonhosted.org/packages/07/c9/032267192e7828520dacb64dfdb1d74f292765f179e467c1cba97687f17d/kiwisolver-1.4.7-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:84ec80df401cfee1457063732d90022f93951944b5b58975d34ab56bb150dfb3", size = 2262191, upload-time = "2024-09-04T09:04:05.969Z" }, + { url = "https://files.pythonhosted.org/packages/6c/ad/db0aedb638a58b2951da46ddaeecf204be8b4f5454df020d850c7fa8dca8/kiwisolver-1.4.7-cp310-cp310-win32.whl", hash = "sha256:71bb308552200fb2c195e35ef05de12f0c878c07fc91c270eb3d6e41698c3bcc", size = 46644, upload-time = "2024-09-04T09:04:07.408Z" }, + { url = "https://files.pythonhosted.org/packages/12/ca/d0f7b7ffbb0be1e7c2258b53554efec1fd652921f10d7d85045aff93ab61/kiwisolver-1.4.7-cp310-cp310-win_amd64.whl", hash = "sha256:44756f9fd339de0fb6ee4f8c1696cfd19b2422e0d70b4cefc1cc7f1f64045a8c", size = 55877, upload-time = "2024-09-04T09:04:08.869Z" }, + { url = "https://files.pythonhosted.org/packages/97/6c/cfcc128672f47a3e3c0d918ecb67830600078b025bfc32d858f2e2d5c6a4/kiwisolver-1.4.7-cp310-cp310-win_arm64.whl", hash = "sha256:78a42513018c41c2ffd262eb676442315cbfe3c44eed82385c2ed043bc63210a", size = 48347, upload-time = "2024-09-04T09:04:10.106Z" }, + { url = "https://files.pythonhosted.org/packages/e9/44/77429fa0a58f941d6e1c58da9efe08597d2e86bf2b2cce6626834f49d07b/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:d2b0e12a42fb4e72d509fc994713d099cbb15ebf1103545e8a45f14da2dfca54", size = 122442, upload-time = "2024-09-04T09:04:11.432Z" }, + { url = "https://files.pythonhosted.org/packages/e5/20/8c75caed8f2462d63c7fd65e16c832b8f76cda331ac9e615e914ee80bac9/kiwisolver-1.4.7-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:2a8781ac3edc42ea4b90bc23e7d37b665d89423818e26eb6df90698aa2287c95", size = 65762, upload-time = "2024-09-04T09:04:12.468Z" }, + { url = "https://files.pythonhosted.org/packages/f4/98/fe010f15dc7230f45bc4cf367b012d651367fd203caaa992fd1f5963560e/kiwisolver-1.4.7-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:46707a10836894b559e04b0fd143e343945c97fd170d69a2d26d640b4e297935", size = 64319, upload-time = "2024-09-04T09:04:13.635Z" }, + { url = "https://files.pythonhosted.org/packages/8b/1b/b5d618f4e58c0675654c1e5051bcf42c776703edb21c02b8c74135541f60/kiwisolver-1.4.7-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ef97b8df011141c9b0f6caf23b29379f87dd13183c978a30a3c546d2c47314cb", size = 1334260, upload-time = "2024-09-04T09:04:14.878Z" }, + { url = "https://files.pythonhosted.org/packages/b8/01/946852b13057a162a8c32c4c8d2e9ed79f0bb5d86569a40c0b5fb103e373/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3ab58c12a2cd0fc769089e6d38466c46d7f76aced0a1f54c77652446733d2d02", size = 1426589, upload-time = "2024-09-04T09:04:16.514Z" }, + { url = "https://files.pythonhosted.org/packages/70/d1/c9f96df26b459e15cf8a965304e6e6f4eb291e0f7a9460b4ad97b047561e/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:803b8e1459341c1bb56d1c5c010406d5edec8a0713a0945851290a7930679b51", size = 1541080, upload-time = "2024-09-04T09:04:18.322Z" }, + { url = "https://files.pythonhosted.org/packages/d3/73/2686990eb8b02d05f3de759d6a23a4ee7d491e659007dd4c075fede4b5d0/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f9a9e8a507420fe35992ee9ecb302dab68550dedc0da9e2880dd88071c5fb052", size = 1470049, upload-time = "2024-09-04T09:04:20.266Z" }, + { url = "https://files.pythonhosted.org/packages/a7/4b/2db7af3ed3af7c35f388d5f53c28e155cd402a55432d800c543dc6deb731/kiwisolver-1.4.7-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18077b53dc3bb490e330669a99920c5e6a496889ae8c63b58fbc57c3d7f33a18", size = 1426376, upload-time = "2024-09-04T09:04:22.419Z" }, + { url = "https://files.pythonhosted.org/packages/05/83/2857317d04ea46dc5d115f0df7e676997bbd968ced8e2bd6f7f19cfc8d7f/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6af936f79086a89b3680a280c47ea90b4df7047b5bdf3aa5c524bbedddb9e545", size = 2222231, upload-time = "2024-09-04T09:04:24.526Z" }, + { url = "https://files.pythonhosted.org/packages/0d/b5/866f86f5897cd4ab6d25d22e403404766a123f138bd6a02ecb2cdde52c18/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:3abc5b19d24af4b77d1598a585b8a719beb8569a71568b66f4ebe1fb0449460b", size = 2368634, upload-time = "2024-09-04T09:04:25.899Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ee/73de8385403faba55f782a41260210528fe3273d0cddcf6d51648202d6d0/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:933d4de052939d90afbe6e9d5273ae05fb836cc86c15b686edd4b3560cc0ee36", size = 2329024, upload-time = "2024-09-04T09:04:28.523Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/cd101d8cd2cdfaa42dc06c433df17c8303d31129c9fdd16c0ea37672af91/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:65e720d2ab2b53f1f72fb5da5fb477455905ce2c88aaa671ff0a447c2c80e8e3", size = 2468484, upload-time = "2024-09-04T09:04:30.547Z" }, + { url = "https://files.pythonhosted.org/packages/e1/72/84f09d45a10bc57a40bb58b81b99d8f22b58b2040c912b7eb97ebf625bf2/kiwisolver-1.4.7-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:3bf1ed55088f214ba6427484c59553123fdd9b218a42bbc8c6496d6754b1e523", size = 2284078, upload-time = "2024-09-04T09:04:33.218Z" }, + { url = "https://files.pythonhosted.org/packages/d2/d4/71828f32b956612dc36efd7be1788980cb1e66bfb3706e6dec9acad9b4f9/kiwisolver-1.4.7-cp311-cp311-win32.whl", hash = "sha256:4c00336b9dd5ad96d0a558fd18a8b6f711b7449acce4c157e7343ba92dd0cf3d", size = 46645, upload-time = "2024-09-04T09:04:34.371Z" }, + { url = "https://files.pythonhosted.org/packages/a1/65/d43e9a20aabcf2e798ad1aff6c143ae3a42cf506754bcb6a7ed8259c8425/kiwisolver-1.4.7-cp311-cp311-win_amd64.whl", hash = "sha256:929e294c1ac1e9f615c62a4e4313ca1823ba37326c164ec720a803287c4c499b", size = 56022, upload-time = "2024-09-04T09:04:35.786Z" }, + { url = "https://files.pythonhosted.org/packages/35/b3/9f75a2e06f1b4ca00b2b192bc2b739334127d27f1d0625627ff8479302ba/kiwisolver-1.4.7-cp311-cp311-win_arm64.whl", hash = "sha256:e33e8fbd440c917106b237ef1a2f1449dfbb9b6f6e1ce17c94cd6a1e0d438376", size = 48536, upload-time = "2024-09-04T09:04:37.525Z" }, + { url = "https://files.pythonhosted.org/packages/97/9c/0a11c714cf8b6ef91001c8212c4ef207f772dd84540104952c45c1f0a249/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:5360cc32706dab3931f738d3079652d20982511f7c0ac5711483e6eab08efff2", size = 121808, upload-time = "2024-09-04T09:04:38.637Z" }, + { url = "https://files.pythonhosted.org/packages/f2/d8/0fe8c5f5d35878ddd135f44f2af0e4e1d379e1c7b0716f97cdcb88d4fd27/kiwisolver-1.4.7-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:942216596dc64ddb25adb215c3c783215b23626f8d84e8eff8d6d45c3f29f75a", size = 65531, upload-time = "2024-09-04T09:04:39.694Z" }, + { url = "https://files.pythonhosted.org/packages/80/c5/57fa58276dfdfa612241d640a64ca2f76adc6ffcebdbd135b4ef60095098/kiwisolver-1.4.7-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:48b571ecd8bae15702e4f22d3ff6a0f13e54d3d00cd25216d5e7f658242065ee", size = 63894, upload-time = "2024-09-04T09:04:41.6Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e9/26d3edd4c4ad1c5b891d8747a4f81b1b0aba9fb9721de6600a4adc09773b/kiwisolver-1.4.7-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ad42ba922c67c5f219097b28fae965e10045ddf145d2928bfac2eb2e17673640", size = 1369296, upload-time = "2024-09-04T09:04:42.886Z" }, + { url = "https://files.pythonhosted.org/packages/b6/67/3f4850b5e6cffb75ec40577ddf54f7b82b15269cc5097ff2e968ee32ea7d/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:612a10bdae23404a72941a0fc8fa2660c6ea1217c4ce0dbcab8a8f6543ea9e7f", size = 1461450, upload-time = "2024-09-04T09:04:46.284Z" }, + { url = "https://files.pythonhosted.org/packages/52/be/86cbb9c9a315e98a8dc6b1d23c43cffd91d97d49318854f9c37b0e41cd68/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9e838bba3a3bac0fe06d849d29772eb1afb9745a59710762e4ba3f4cb8424483", size = 1579168, upload-time = "2024-09-04T09:04:47.91Z" }, + { url = "https://files.pythonhosted.org/packages/0f/00/65061acf64bd5fd34c1f4ae53f20b43b0a017a541f242a60b135b9d1e301/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:22f499f6157236c19f4bbbd472fa55b063db77a16cd74d49afe28992dff8c258", size = 1507308, upload-time = "2024-09-04T09:04:49.465Z" }, + { url = "https://files.pythonhosted.org/packages/21/e4/c0b6746fd2eb62fe702118b3ca0cb384ce95e1261cfada58ff693aeec08a/kiwisolver-1.4.7-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:693902d433cf585133699972b6d7c42a8b9f8f826ebcaf0132ff55200afc599e", size = 1464186, upload-time = "2024-09-04T09:04:50.949Z" }, + { url = "https://files.pythonhosted.org/packages/0a/0f/529d0a9fffb4d514f2782c829b0b4b371f7f441d61aa55f1de1c614c4ef3/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:4e77f2126c3e0b0d055f44513ed349038ac180371ed9b52fe96a32aa071a5107", size = 2247877, upload-time = "2024-09-04T09:04:52.388Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e1/66603ad779258843036d45adcbe1af0d1a889a07af4635f8b4ec7dccda35/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:657a05857bda581c3656bfc3b20e353c232e9193eb167766ad2dc58b56504948", size = 2404204, upload-time = "2024-09-04T09:04:54.385Z" }, + { url = "https://files.pythonhosted.org/packages/8d/61/de5fb1ca7ad1f9ab7970e340a5b833d735df24689047de6ae71ab9d8d0e7/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4bfa75a048c056a411f9705856abfc872558e33c055d80af6a380e3658766038", size = 2352461, upload-time = "2024-09-04T09:04:56.307Z" }, + { url = "https://files.pythonhosted.org/packages/ba/d2/0edc00a852e369827f7e05fd008275f550353f1f9bcd55db9363d779fc63/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:34ea1de54beef1c104422d210c47c7d2a4999bdecf42c7b5718fbe59a4cac383", size = 2501358, upload-time = "2024-09-04T09:04:57.922Z" }, + { url = "https://files.pythonhosted.org/packages/84/15/adc15a483506aec6986c01fb7f237c3aec4d9ed4ac10b756e98a76835933/kiwisolver-1.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:90da3b5f694b85231cf93586dad5e90e2d71b9428f9aad96952c99055582f520", size = 2314119, upload-time = "2024-09-04T09:04:59.332Z" }, + { url = "https://files.pythonhosted.org/packages/36/08/3a5bb2c53c89660863a5aa1ee236912269f2af8762af04a2e11df851d7b2/kiwisolver-1.4.7-cp312-cp312-win32.whl", hash = "sha256:18e0cca3e008e17fe9b164b55735a325140a5a35faad8de92dd80265cd5eb80b", size = 46367, upload-time = "2024-09-04T09:05:00.804Z" }, + { url = "https://files.pythonhosted.org/packages/19/93/c05f0a6d825c643779fc3c70876bff1ac221f0e31e6f701f0e9578690d70/kiwisolver-1.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:58cb20602b18f86f83a5c87d3ee1c766a79c0d452f8def86d925e6c60fbf7bfb", size = 55884, upload-time = "2024-09-04T09:05:01.924Z" }, + { url = "https://files.pythonhosted.org/packages/d2/f9/3828d8f21b6de4279f0667fb50a9f5215e6fe57d5ec0d61905914f5b6099/kiwisolver-1.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:f5a8b53bdc0b3961f8b6125e198617c40aeed638b387913bf1ce78afb1b0be2a", size = 48528, upload-time = "2024-09-04T09:05:02.983Z" }, + { url = "https://files.pythonhosted.org/packages/c4/06/7da99b04259b0f18b557a4effd1b9c901a747f7fdd84cf834ccf520cb0b2/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2e6039dcbe79a8e0f044f1c39db1986a1b8071051efba3ee4d74f5b365f5226e", size = 121913, upload-time = "2024-09-04T09:05:04.072Z" }, + { url = "https://files.pythonhosted.org/packages/97/f5/b8a370d1aa593c17882af0a6f6755aaecd643640c0ed72dcfd2eafc388b9/kiwisolver-1.4.7-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:a1ecf0ac1c518487d9d23b1cd7139a6a65bc460cd101ab01f1be82ecf09794b6", size = 65627, upload-time = "2024-09-04T09:05:05.119Z" }, + { url = "https://files.pythonhosted.org/packages/2a/fc/6c0374f7503522539e2d4d1b497f5ebad3f8ed07ab51aed2af988dd0fb65/kiwisolver-1.4.7-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7ab9ccab2b5bd5702ab0803676a580fffa2aa178c2badc5557a84cc943fcf750", size = 63888, upload-time = "2024-09-04T09:05:06.191Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3e/0b7172793d0f41cae5c923492da89a2ffcd1adf764c16159ca047463ebd3/kiwisolver-1.4.7-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f816dd2277f8d63d79f9c8473a79fe54047bc0467754962840782c575522224d", size = 1369145, upload-time = "2024-09-04T09:05:07.919Z" }, + { url = "https://files.pythonhosted.org/packages/77/92/47d050d6f6aced2d634258123f2688fbfef8ded3c5baf2c79d94d91f1f58/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf8bcc23ceb5a1b624572a1623b9f79d2c3b337c8c455405ef231933a10da379", size = 1461448, upload-time = "2024-09-04T09:05:10.01Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1b/8f80b18e20b3b294546a1adb41701e79ae21915f4175f311a90d042301cf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dea0bf229319828467d7fca8c7c189780aa9ff679c94539eed7532ebe33ed37c", size = 1578750, upload-time = "2024-09-04T09:05:11.598Z" }, + { url = "https://files.pythonhosted.org/packages/a4/fe/fe8e72f3be0a844f257cadd72689c0848c6d5c51bc1d60429e2d14ad776e/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7c06a4c7cf15ec739ce0e5971b26c93638730090add60e183530d70848ebdd34", size = 1507175, upload-time = "2024-09-04T09:05:13.22Z" }, + { url = "https://files.pythonhosted.org/packages/39/fa/cdc0b6105d90eadc3bee525fecc9179e2b41e1ce0293caaf49cb631a6aaf/kiwisolver-1.4.7-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:913983ad2deb14e66d83c28b632fd35ba2b825031f2fa4ca29675e665dfecbe1", size = 1463963, upload-time = "2024-09-04T09:05:15.925Z" }, + { url = "https://files.pythonhosted.org/packages/6e/5c/0c03c4e542720c6177d4f408e56d1c8315899db72d46261a4e15b8b33a41/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:5337ec7809bcd0f424c6b705ecf97941c46279cf5ed92311782c7c9c2026f07f", size = 2248220, upload-time = "2024-09-04T09:05:17.434Z" }, + { url = "https://files.pythonhosted.org/packages/3d/ee/55ef86d5a574f4e767df7da3a3a7ff4954c996e12d4fbe9c408170cd7dcc/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4c26ed10c4f6fa6ddb329a5120ba3b6db349ca192ae211e882970bfc9d91420b", size = 2404463, upload-time = "2024-09-04T09:05:18.997Z" }, + { url = "https://files.pythonhosted.org/packages/0f/6d/73ad36170b4bff4825dc588acf4f3e6319cb97cd1fb3eb04d9faa6b6f212/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c619b101e6de2222c1fcb0531e1b17bbffbe54294bfba43ea0d411d428618c27", size = 2352842, upload-time = "2024-09-04T09:05:21.299Z" }, + { url = "https://files.pythonhosted.org/packages/0b/16/fa531ff9199d3b6473bb4d0f47416cdb08d556c03b8bc1cccf04e756b56d/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:073a36c8273647592ea332e816e75ef8da5c303236ec0167196793eb1e34657a", size = 2501635, upload-time = "2024-09-04T09:05:23.588Z" }, + { url = "https://files.pythonhosted.org/packages/78/7e/aa9422e78419db0cbe75fb86d8e72b433818f2e62e2e394992d23d23a583/kiwisolver-1.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3ce6b2b0231bda412463e152fc18335ba32faf4e8c23a754ad50ffa70e4091ee", size = 2314556, upload-time = "2024-09-04T09:05:25.907Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b2/15f7f556df0a6e5b3772a1e076a9d9f6c538ce5f05bd590eca8106508e06/kiwisolver-1.4.7-cp313-cp313-win32.whl", hash = "sha256:f4c9aee212bc89d4e13f58be11a56cc8036cabad119259d12ace14b34476fd07", size = 46364, upload-time = "2024-09-04T09:05:27.184Z" }, + { url = "https://files.pythonhosted.org/packages/0b/db/32e897e43a330eee8e4770bfd2737a9584b23e33587a0812b8e20aac38f7/kiwisolver-1.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:8a3ec5aa8e38fc4c8af308917ce12c536f1c88452ce554027e55b22cbbfbff76", size = 55887, upload-time = "2024-09-04T09:05:28.372Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a4/df2bdca5270ca85fd25253049eb6708d4127be2ed0e5c2650217450b59e9/kiwisolver-1.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:76c8094ac20ec259471ac53e774623eb62e6e1f56cd8690c67ce6ce4fcb05650", size = 48530, upload-time = "2024-09-04T09:05:30.225Z" }, + { url = "https://files.pythonhosted.org/packages/11/88/37ea0ea64512997b13d69772db8dcdc3bfca5442cda3a5e4bb943652ee3e/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3f9362ecfca44c863569d3d3c033dbe8ba452ff8eed6f6b5806382741a1334bd", size = 122449, upload-time = "2024-09-04T09:05:55.311Z" }, + { url = "https://files.pythonhosted.org/packages/4e/45/5a5c46078362cb3882dcacad687c503089263c017ca1241e0483857791eb/kiwisolver-1.4.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:e8df2eb9b2bac43ef8b082e06f750350fbbaf2887534a5be97f6cf07b19d9583", size = 65757, upload-time = "2024-09-04T09:05:56.906Z" }, + { url = "https://files.pythonhosted.org/packages/8a/be/a6ae58978772f685d48dd2e84460937761c53c4bbd84e42b0336473d9775/kiwisolver-1.4.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:f32d6edbc638cde7652bd690c3e728b25332acbadd7cad670cc4a02558d9c417", size = 64312, upload-time = "2024-09-04T09:05:58.384Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/18ef6f452d311e1e1eb180c9bf5589187fa1f042db877e6fe443ef10099c/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:e2e6c39bd7b9372b0be21456caab138e8e69cc0fc1190a9dfa92bd45a1e6e904", size = 1626966, upload-time = "2024-09-04T09:05:59.855Z" }, + { url = "https://files.pythonhosted.org/packages/21/b1/40655f6c3fa11ce740e8a964fa8e4c0479c87d6a7944b95af799c7a55dfe/kiwisolver-1.4.7-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dda56c24d869b1193fcc763f1284b9126550eaf84b88bbc7256e15028f19188a", size = 1607044, upload-time = "2024-09-04T09:06:02.16Z" }, + { url = "https://files.pythonhosted.org/packages/fd/93/af67dbcfb9b3323bbd2c2db1385a7139d8f77630e4a37bb945b57188eb2d/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79849239c39b5e1fd906556c474d9b0439ea6792b637511f3fe3a41158d89ca8", size = 1391879, upload-time = "2024-09-04T09:06:03.908Z" }, + { url = "https://files.pythonhosted.org/packages/40/6f/d60770ef98e77b365d96061d090c0cd9e23418121c55fff188fa4bdf0b54/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5e3bc157fed2a4c02ec468de4ecd12a6e22818d4f09cde2c31ee3226ffbefab2", size = 1504751, upload-time = "2024-09-04T09:06:05.58Z" }, + { url = "https://files.pythonhosted.org/packages/fa/3a/5f38667d313e983c432f3fcd86932177519ed8790c724e07d77d1de0188a/kiwisolver-1.4.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3da53da805b71e41053dc670f9a820d1157aae77b6b944e08024d17bcd51ef88", size = 1436990, upload-time = "2024-09-04T09:06:08.126Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/1520301a47326e6a6043b502647e42892be33b3f051e9791cc8bb43f1a32/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:8705f17dfeb43139a692298cb6637ee2e59c0194538153e83e9ee0c75c2eddde", size = 2191122, upload-time = "2024-09-04T09:06:10.345Z" }, + { url = "https://files.pythonhosted.org/packages/cf/c4/eb52da300c166239a2233f1f9c4a1b767dfab98fae27681bfb7ea4873cb6/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:82a5c2f4b87c26bb1a0ef3d16b5c4753434633b83d365cc0ddf2770c93829e3c", size = 2338126, upload-time = "2024-09-04T09:06:12.321Z" }, + { url = "https://files.pythonhosted.org/packages/1a/cb/42b92fd5eadd708dd9107c089e817945500685f3437ce1fd387efebc6d6e/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ce8be0466f4c0d585cdb6c1e2ed07232221df101a4c6f28821d2aa754ca2d9e2", size = 2298313, upload-time = "2024-09-04T09:06:14.562Z" }, + { url = "https://files.pythonhosted.org/packages/4f/eb/be25aa791fe5fc75a8b1e0c965e00f942496bc04635c9aae8035f6b76dcd/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:409afdfe1e2e90e6ee7fc896f3df9a7fec8e793e58bfa0d052c8a82f99c37abb", size = 2437784, upload-time = "2024-09-04T09:06:16.767Z" }, + { url = "https://files.pythonhosted.org/packages/c5/22/30a66be7f3368d76ff95689e1c2e28d382383952964ab15330a15d8bfd03/kiwisolver-1.4.7-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:5b9c3f4ee0b9a439d2415012bd1b1cc2df59e4d6a9939f4d669241d30b414327", size = 2253988, upload-time = "2024-09-04T09:06:18.705Z" }, + { url = "https://files.pythonhosted.org/packages/35/d3/5f2ecb94b5211c8a04f218a76133cc8d6d153b0f9cd0b45fad79907f0689/kiwisolver-1.4.7-cp39-cp39-win32.whl", hash = "sha256:a79ae34384df2b615eefca647a2873842ac3b596418032bef9a7283675962644", size = 46980, upload-time = "2024-09-04T09:06:20.106Z" }, + { url = "https://files.pythonhosted.org/packages/ef/17/cd10d020578764ea91740204edc6b3236ed8106228a46f568d716b11feb2/kiwisolver-1.4.7-cp39-cp39-win_amd64.whl", hash = "sha256:cf0438b42121a66a3a667de17e779330fc0f20b0d97d59d2f2121e182b0505e4", size = 55847, upload-time = "2024-09-04T09:06:21.407Z" }, + { url = "https://files.pythonhosted.org/packages/91/84/32232502020bd78d1d12be7afde15811c64a95ed1f606c10456db4e4c3ac/kiwisolver-1.4.7-cp39-cp39-win_arm64.whl", hash = "sha256:764202cc7e70f767dab49e8df52c7455e8de0df5d858fa801a11aa0d882ccf3f", size = 48494, upload-time = "2024-09-04T09:06:22.648Z" }, + { url = "https://files.pythonhosted.org/packages/ac/59/741b79775d67ab67ced9bb38552da688c0305c16e7ee24bba7a2be253fb7/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:94252291e3fe68001b1dd747b4c0b3be12582839b95ad4d1b641924d68fd4643", size = 59491, upload-time = "2024-09-04T09:06:24.188Z" }, + { url = "https://files.pythonhosted.org/packages/58/cc/fb239294c29a5656e99e3527f7369b174dd9cc7c3ef2dea7cb3c54a8737b/kiwisolver-1.4.7-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5b7dfa3b546da08a9f622bb6becdb14b3e24aaa30adba66749d38f3cc7ea9706", size = 57648, upload-time = "2024-09-04T09:06:25.559Z" }, + { url = "https://files.pythonhosted.org/packages/3b/ef/2f009ac1f7aab9f81efb2d837301d255279d618d27b6015780115ac64bdd/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd3de6481f4ed8b734da5df134cd5a6a64fe32124fe83dde1e5b5f29fe30b1e6", size = 84257, upload-time = "2024-09-04T09:06:27.038Z" }, + { url = "https://files.pythonhosted.org/packages/81/e1/c64f50987f85b68b1c52b464bb5bf73e71570c0f7782d626d1eb283ad620/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a91b5f9f1205845d488c928e8570dcb62b893372f63b8b6e98b863ebd2368ff2", size = 80906, upload-time = "2024-09-04T09:06:28.48Z" }, + { url = "https://files.pythonhosted.org/packages/fd/71/1687c5c0a0be2cee39a5c9c389e546f9c6e215e46b691d00d9f646892083/kiwisolver-1.4.7-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40fa14dbd66b8b8f470d5fc79c089a66185619d31645f9b0773b88b19f7223c4", size = 79951, upload-time = "2024-09-04T09:06:29.966Z" }, + { url = "https://files.pythonhosted.org/packages/ea/8b/d7497df4a1cae9367adf21665dd1f896c2a7aeb8769ad77b662c5e2bcce7/kiwisolver-1.4.7-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:eb542fe7933aa09d8d8f9d9097ef37532a7df6497819d16efe4359890a2f417a", size = 55715, upload-time = "2024-09-04T09:06:31.489Z" }, + { url = "https://files.pythonhosted.org/packages/d5/df/ce37d9b26f07ab90880923c94d12a6ff4d27447096b4c849bfc4339ccfdf/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:8b01aac285f91ca889c800042c35ad3b239e704b150cfd3382adfc9dcc780e39", size = 58666, upload-time = "2024-09-04T09:06:43.756Z" }, + { url = "https://files.pythonhosted.org/packages/b0/d3/e4b04f43bc629ac8e186b77b2b1a251cdfa5b7610fa189dc0db622672ce6/kiwisolver-1.4.7-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:48be928f59a1f5c8207154f935334d374e79f2b5d212826307d072595ad76a2e", size = 57088, upload-time = "2024-09-04T09:06:45.406Z" }, + { url = "https://files.pythonhosted.org/packages/30/1c/752df58e2d339e670a535514d2db4fe8c842ce459776b8080fbe08ebb98e/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f37cfe618a117e50d8c240555331160d73d0411422b59b5ee217843d7b693608", size = 84321, upload-time = "2024-09-04T09:06:47.557Z" }, + { url = "https://files.pythonhosted.org/packages/f0/f8/fe6484e847bc6e238ec9f9828089fb2c0bb53f2f5f3a79351fde5b565e4f/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:599b5c873c63a1f6ed7eead644a8a380cfbdf5db91dcb6f85707aaab213b1674", size = 80776, upload-time = "2024-09-04T09:06:49.235Z" }, + { url = "https://files.pythonhosted.org/packages/9b/57/d7163c0379f250ef763aba85330a19feefb5ce6cb541ade853aaba881524/kiwisolver-1.4.7-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:801fa7802e5cfabe3ab0c81a34c323a319b097dfb5004be950482d882f3d7225", size = 79984, upload-time = "2024-09-04T09:06:51.336Z" }, + { url = "https://files.pythonhosted.org/packages/8c/95/4a103776c265d13b3d2cd24fb0494d4e04ea435a8ef97e1b2c026d43250b/kiwisolver-1.4.7-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0c6c43471bc764fad4bc99c5c2d6d16a676b1abf844ca7c8702bdae92df01ee0", size = 55811, upload-time = "2024-09-04T09:06:53.078Z" }, +] + +[[package]] +name = "kiwisolver" +version = "1.4.9" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/5c/3c/85844f1b0feb11ee581ac23fe5fce65cd049a200c1446708cc1b7f922875/kiwisolver-1.4.9.tar.gz", hash = "sha256:c3b22c26c6fd6811b0ae8363b95ca8ce4ea3c202d3d0975b2914310ceb1bcc4d", size = 97564, upload-time = "2025-08-10T21:27:49.279Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/5d/8ce64e36d4e3aac5ca96996457dcf33e34e6051492399a3f1fec5657f30b/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:b4b4d74bda2b8ebf4da5bd42af11d02d04428b2c32846e4c2c93219df8a7987b", size = 124159, upload-time = "2025-08-10T21:25:35.472Z" }, + { url = "https://files.pythonhosted.org/packages/96/1e/22f63ec454874378175a5f435d6ea1363dd33fb2af832c6643e4ccea0dc8/kiwisolver-1.4.9-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fb3b8132019ea572f4611d770991000d7f58127560c4889729248eb5852a102f", size = 66578, upload-time = "2025-08-10T21:25:36.73Z" }, + { url = "https://files.pythonhosted.org/packages/41/4c/1925dcfff47a02d465121967b95151c82d11027d5ec5242771e580e731bd/kiwisolver-1.4.9-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:84fd60810829c27ae375114cd379da1fa65e6918e1da405f356a775d49a62bcf", size = 65312, upload-time = "2025-08-10T21:25:37.658Z" }, + { url = "https://files.pythonhosted.org/packages/d4/42/0f333164e6307a0687d1eb9ad256215aae2f4bd5d28f4653d6cd319a3ba3/kiwisolver-1.4.9-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b78efa4c6e804ecdf727e580dbb9cba85624d2e1c6b5cb059c66290063bd99a9", size = 1628458, upload-time = "2025-08-10T21:25:39.067Z" }, + { url = "https://files.pythonhosted.org/packages/86/b6/2dccb977d651943995a90bfe3495c2ab2ba5cd77093d9f2318a20c9a6f59/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4efec7bcf21671db6a3294ff301d2fc861c31faa3c8740d1a94689234d1b415", size = 1225640, upload-time = "2025-08-10T21:25:40.489Z" }, + { url = "https://files.pythonhosted.org/packages/50/2b/362ebd3eec46c850ccf2bfe3e30f2fc4c008750011f38a850f088c56a1c6/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:90f47e70293fc3688b71271100a1a5453aa9944a81d27ff779c108372cf5567b", size = 1244074, upload-time = "2025-08-10T21:25:42.221Z" }, + { url = "https://files.pythonhosted.org/packages/6f/bb/f09a1e66dab8984773d13184a10a29fe67125337649d26bdef547024ed6b/kiwisolver-1.4.9-cp310-cp310-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:8fdca1def57a2e88ef339de1737a1449d6dbf5fab184c54a1fca01d541317154", size = 1293036, upload-time = "2025-08-10T21:25:43.801Z" }, + { url = "https://files.pythonhosted.org/packages/ea/01/11ecf892f201cafda0f68fa59212edaea93e96c37884b747c181303fccd1/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:9cf554f21be770f5111a1690d42313e140355e687e05cf82cb23d0a721a64a48", size = 2175310, upload-time = "2025-08-10T21:25:45.045Z" }, + { url = "https://files.pythonhosted.org/packages/7f/5f/bfe11d5b934f500cc004314819ea92427e6e5462706a498c1d4fc052e08f/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fc1795ac5cd0510207482c3d1d3ed781143383b8cfd36f5c645f3897ce066220", size = 2270943, upload-time = "2025-08-10T21:25:46.393Z" }, + { url = "https://files.pythonhosted.org/packages/3d/de/259f786bf71f1e03e73d87e2db1a9a3bcab64d7b4fd780167123161630ad/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:ccd09f20ccdbbd341b21a67ab50a119b64a403b09288c27481575105283c1586", size = 2440488, upload-time = "2025-08-10T21:25:48.074Z" }, + { url = "https://files.pythonhosted.org/packages/1b/76/c989c278faf037c4d3421ec07a5c452cd3e09545d6dae7f87c15f54e4edf/kiwisolver-1.4.9-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:540c7c72324d864406a009d72f5d6856f49693db95d1fbb46cf86febef873634", size = 2246787, upload-time = "2025-08-10T21:25:49.442Z" }, + { url = "https://files.pythonhosted.org/packages/a2/55/c2898d84ca440852e560ca9f2a0d28e6e931ac0849b896d77231929900e7/kiwisolver-1.4.9-cp310-cp310-win_amd64.whl", hash = "sha256:ede8c6d533bc6601a47ad4046080d36b8fc99f81e6f1c17b0ac3c2dc91ac7611", size = 73730, upload-time = "2025-08-10T21:25:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/e8/09/486d6ac523dd33b80b368247f238125d027964cfacb45c654841e88fb2ae/kiwisolver-1.4.9-cp310-cp310-win_arm64.whl", hash = "sha256:7b4da0d01ac866a57dd61ac258c5607b4cd677f63abaec7b148354d2b2cdd536", size = 65036, upload-time = "2025-08-10T21:25:52.063Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ab/c80b0d5a9d8a1a65f4f815f2afff9798b12c3b9f31f1d304dd233dd920e2/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:eb14a5da6dc7642b0f3a18f13654847cd8b7a2550e2645a5bda677862b03ba16", size = 124167, upload-time = "2025-08-10T21:25:53.403Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c0/27fe1a68a39cf62472a300e2879ffc13c0538546c359b86f149cc19f6ac3/kiwisolver-1.4.9-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:39a219e1c81ae3b103643d2aedb90f1ef22650deb266ff12a19e7773f3e5f089", size = 66579, upload-time = "2025-08-10T21:25:54.79Z" }, + { url = "https://files.pythonhosted.org/packages/31/a2/a12a503ac1fd4943c50f9822678e8015a790a13b5490354c68afb8489814/kiwisolver-1.4.9-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2405a7d98604b87f3fc28b1716783534b1b4b8510d8142adca34ee0bc3c87543", size = 65309, upload-time = "2025-08-10T21:25:55.76Z" }, + { url = "https://files.pythonhosted.org/packages/66/e1/e533435c0be77c3f64040d68d7a657771194a63c279f55573188161e81ca/kiwisolver-1.4.9-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:dc1ae486f9abcef254b5618dfb4113dd49f94c68e3e027d03cf0143f3f772b61", size = 1435596, upload-time = "2025-08-10T21:25:56.861Z" }, + { url = "https://files.pythonhosted.org/packages/67/1e/51b73c7347f9aabdc7215aa79e8b15299097dc2f8e67dee2b095faca9cb0/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8a1f570ce4d62d718dce3f179ee78dac3b545ac16c0c04bb363b7607a949c0d1", size = 1246548, upload-time = "2025-08-10T21:25:58.246Z" }, + { url = "https://files.pythonhosted.org/packages/21/aa/72a1c5d1e430294f2d32adb9542719cfb441b5da368d09d268c7757af46c/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:cb27e7b78d716c591e88e0a09a2139c6577865d7f2e152488c2cc6257f460872", size = 1263618, upload-time = "2025-08-10T21:25:59.857Z" }, + { url = "https://files.pythonhosted.org/packages/a3/af/db1509a9e79dbf4c260ce0cfa3903ea8945f6240e9e59d1e4deb731b1a40/kiwisolver-1.4.9-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:15163165efc2f627eb9687ea5f3a28137217d217ac4024893d753f46bce9de26", size = 1317437, upload-time = "2025-08-10T21:26:01.105Z" }, + { url = "https://files.pythonhosted.org/packages/e0/f2/3ea5ee5d52abacdd12013a94130436e19969fa183faa1e7c7fbc89e9a42f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bdee92c56a71d2b24c33a7d4c2856bd6419d017e08caa7802d2963870e315028", size = 2195742, upload-time = "2025-08-10T21:26:02.675Z" }, + { url = "https://files.pythonhosted.org/packages/6f/9b/1efdd3013c2d9a2566aa6a337e9923a00590c516add9a1e89a768a3eb2fc/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:412f287c55a6f54b0650bd9b6dce5aceddb95864a1a90c87af16979d37c89771", size = 2290810, upload-time = "2025-08-10T21:26:04.009Z" }, + { url = "https://files.pythonhosted.org/packages/fb/e5/cfdc36109ae4e67361f9bc5b41323648cb24a01b9ade18784657e022e65f/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:2c93f00dcba2eea70af2be5f11a830a742fe6b579a1d4e00f47760ef13be247a", size = 2461579, upload-time = "2025-08-10T21:26:05.317Z" }, + { url = "https://files.pythonhosted.org/packages/62/86/b589e5e86c7610842213994cdea5add00960076bef4ae290c5fa68589cac/kiwisolver-1.4.9-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f117e1a089d9411663a3207ba874f31be9ac8eaa5b533787024dc07aeb74f464", size = 2268071, upload-time = "2025-08-10T21:26:06.686Z" }, + { url = "https://files.pythonhosted.org/packages/3b/c6/f8df8509fd1eee6c622febe54384a96cfaf4d43bf2ccec7a0cc17e4715c9/kiwisolver-1.4.9-cp311-cp311-win_amd64.whl", hash = "sha256:be6a04e6c79819c9a8c2373317d19a96048e5a3f90bec587787e86a1153883c2", size = 73840, upload-time = "2025-08-10T21:26:07.94Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2d/16e0581daafd147bc11ac53f032a2b45eabac897f42a338d0a13c1e5c436/kiwisolver-1.4.9-cp311-cp311-win_arm64.whl", hash = "sha256:0ae37737256ba2de764ddc12aed4956460277f00c4996d51a197e72f62f5eec7", size = 65159, upload-time = "2025-08-10T21:26:09.048Z" }, + { url = "https://files.pythonhosted.org/packages/86/c9/13573a747838aeb1c76e3267620daa054f4152444d1f3d1a2324b78255b5/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:ac5a486ac389dddcc5bef4f365b6ae3ffff2c433324fb38dd35e3fab7c957999", size = 123686, upload-time = "2025-08-10T21:26:10.034Z" }, + { url = "https://files.pythonhosted.org/packages/51/ea/2ecf727927f103ffd1739271ca19c424d0e65ea473fbaeea1c014aea93f6/kiwisolver-1.4.9-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f2ba92255faa7309d06fe44c3a4a97efe1c8d640c2a79a5ef728b685762a6fd2", size = 66460, upload-time = "2025-08-10T21:26:11.083Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/51f5464373ce2aeb5194508298a508b6f21d3867f499556263c64c621914/kiwisolver-1.4.9-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:4a2899935e724dd1074cb568ce7ac0dce28b2cd6ab539c8e001a8578eb106d14", size = 64952, upload-time = "2025-08-10T21:26:12.058Z" }, + { url = "https://files.pythonhosted.org/packages/70/90/6d240beb0f24b74371762873e9b7f499f1e02166a2d9c5801f4dbf8fa12e/kiwisolver-1.4.9-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f6008a4919fdbc0b0097089f67a1eb55d950ed7e90ce2cc3e640abadd2757a04", size = 1474756, upload-time = "2025-08-10T21:26:13.096Z" }, + { url = "https://files.pythonhosted.org/packages/12/42/f36816eaf465220f683fb711efdd1bbf7a7005a2473d0e4ed421389bd26c/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:67bb8b474b4181770f926f7b7d2f8c0248cbcb78b660fdd41a47054b28d2a752", size = 1276404, upload-time = "2025-08-10T21:26:14.457Z" }, + { url = "https://files.pythonhosted.org/packages/2e/64/bc2de94800adc830c476dce44e9b40fd0809cddeef1fde9fcf0f73da301f/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:2327a4a30d3ee07d2fbe2e7933e8a37c591663b96ce42a00bc67461a87d7df77", size = 1294410, upload-time = "2025-08-10T21:26:15.73Z" }, + { url = "https://files.pythonhosted.org/packages/5f/42/2dc82330a70aa8e55b6d395b11018045e58d0bb00834502bf11509f79091/kiwisolver-1.4.9-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7a08b491ec91b1d5053ac177afe5290adacf1f0f6307d771ccac5de30592d198", size = 1343631, upload-time = "2025-08-10T21:26:17.045Z" }, + { url = "https://files.pythonhosted.org/packages/22/fd/f4c67a6ed1aab149ec5a8a401c323cee7a1cbe364381bb6c9c0d564e0e20/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d8fc5c867c22b828001b6a38d2eaeb88160bf5783c6cb4a5e440efc981ce286d", size = 2224963, upload-time = "2025-08-10T21:26:18.737Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/76720bd4cb3713314677d9ec94dcc21ced3f1baf4830adde5bb9b2430a5f/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:3b3115b2581ea35bb6d1f24a4c90af37e5d9b49dcff267eeed14c3893c5b86ab", size = 2321295, upload-time = "2025-08-10T21:26:20.11Z" }, + { url = "https://files.pythonhosted.org/packages/80/19/d3ec0d9ab711242f56ae0dc2fc5d70e298bb4a1f9dfab44c027668c673a1/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:858e4c22fb075920b96a291928cb7dea5644e94c0ee4fcd5af7e865655e4ccf2", size = 2487987, upload-time = "2025-08-10T21:26:21.49Z" }, + { url = "https://files.pythonhosted.org/packages/39/e9/61e4813b2c97e86b6fdbd4dd824bf72d28bcd8d4849b8084a357bc0dd64d/kiwisolver-1.4.9-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ed0fecd28cc62c54b262e3736f8bb2512d8dcfdc2bcf08be5f47f96bf405b145", size = 2291817, upload-time = "2025-08-10T21:26:22.812Z" }, + { url = "https://files.pythonhosted.org/packages/a0/41/85d82b0291db7504da3c2defe35c9a8a5c9803a730f297bd823d11d5fb77/kiwisolver-1.4.9-cp312-cp312-win_amd64.whl", hash = "sha256:f68208a520c3d86ea51acf688a3e3002615a7f0238002cccc17affecc86a8a54", size = 73895, upload-time = "2025-08-10T21:26:24.37Z" }, + { url = "https://files.pythonhosted.org/packages/e2/92/5f3068cf15ee5cb624a0c7596e67e2a0bb2adee33f71c379054a491d07da/kiwisolver-1.4.9-cp312-cp312-win_arm64.whl", hash = "sha256:2c1a4f57df73965f3f14df20b80ee29e6a7930a57d2d9e8491a25f676e197c60", size = 64992, upload-time = "2025-08-10T21:26:25.732Z" }, + { url = "https://files.pythonhosted.org/packages/31/c1/c2686cda909742ab66c7388e9a1a8521a59eb89f8bcfbee28fc980d07e24/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:a5d0432ccf1c7ab14f9949eec60c5d1f924f17c037e9f8b33352fa05799359b8", size = 123681, upload-time = "2025-08-10T21:26:26.725Z" }, + { url = "https://files.pythonhosted.org/packages/ca/f0/f44f50c9f5b1a1860261092e3bc91ecdc9acda848a8b8c6abfda4a24dd5c/kiwisolver-1.4.9-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efb3a45b35622bb6c16dbfab491a8f5a391fe0e9d45ef32f4df85658232ca0e2", size = 66464, upload-time = "2025-08-10T21:26:27.733Z" }, + { url = "https://files.pythonhosted.org/packages/2d/7a/9d90a151f558e29c3936b8a47ac770235f436f2120aca41a6d5f3d62ae8d/kiwisolver-1.4.9-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a12cf6398e8a0a001a059747a1cbf24705e18fe413bc22de7b3d15c67cffe3f", size = 64961, upload-time = "2025-08-10T21:26:28.729Z" }, + { url = "https://files.pythonhosted.org/packages/e9/e9/f218a2cb3a9ffbe324ca29a9e399fa2d2866d7f348ec3a88df87fc248fc5/kiwisolver-1.4.9-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b67e6efbf68e077dd71d1a6b37e43e1a99d0bff1a3d51867d45ee8908b931098", size = 1474607, upload-time = "2025-08-10T21:26:29.798Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/aac26d4c882f14de59041636292bc838db8961373825df23b8eeb807e198/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5656aa670507437af0207645273ccdfee4f14bacd7f7c67a4306d0dcaeaf6eed", size = 1276546, upload-time = "2025-08-10T21:26:31.401Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ad/8bfc1c93d4cc565e5069162f610ba2f48ff39b7de4b5b8d93f69f30c4bed/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:bfc08add558155345129c7803b3671cf195e6a56e7a12f3dde7c57d9b417f525", size = 1294482, upload-time = "2025-08-10T21:26:32.721Z" }, + { url = "https://files.pythonhosted.org/packages/da/f1/6aca55ff798901d8ce403206d00e033191f63d82dd708a186e0ed2067e9c/kiwisolver-1.4.9-cp313-cp313-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:40092754720b174e6ccf9e845d0d8c7d8e12c3d71e7fc35f55f3813e96376f78", size = 1343720, upload-time = "2025-08-10T21:26:34.032Z" }, + { url = "https://files.pythonhosted.org/packages/d1/91/eed031876c595c81d90d0f6fc681ece250e14bf6998c3d7c419466b523b7/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:497d05f29a1300d14e02e6441cf0f5ee81c1ff5a304b0d9fb77423974684e08b", size = 2224907, upload-time = "2025-08-10T21:26:35.824Z" }, + { url = "https://files.pythonhosted.org/packages/e9/ec/4d1925f2e49617b9cca9c34bfa11adefad49d00db038e692a559454dfb2e/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:bdd1a81a1860476eb41ac4bc1e07b3f07259e6d55bbf739b79c8aaedcf512799", size = 2321334, upload-time = "2025-08-10T21:26:37.534Z" }, + { url = "https://files.pythonhosted.org/packages/43/cb/450cd4499356f68802750c6ddc18647b8ea01ffa28f50d20598e0befe6e9/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:e6b93f13371d341afee3be9f7c5964e3fe61d5fa30f6a30eb49856935dfe4fc3", size = 2488313, upload-time = "2025-08-10T21:26:39.191Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/fc76242bd99f885651128a5d4fa6083e5524694b7c88b489b1b55fdc491d/kiwisolver-1.4.9-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d75aa530ccfaa593da12834b86a0724f58bff12706659baa9227c2ccaa06264c", size = 2291970, upload-time = "2025-08-10T21:26:40.828Z" }, + { url = "https://files.pythonhosted.org/packages/75/bd/f1a5d894000941739f2ae1b65a32892349423ad49c2e6d0771d0bad3fae4/kiwisolver-1.4.9-cp313-cp313-win_amd64.whl", hash = "sha256:dd0a578400839256df88c16abddf9ba14813ec5f21362e1fe65022e00c883d4d", size = 73894, upload-time = "2025-08-10T21:26:42.33Z" }, + { url = "https://files.pythonhosted.org/packages/95/38/dce480814d25b99a391abbddadc78f7c117c6da34be68ca8b02d5848b424/kiwisolver-1.4.9-cp313-cp313-win_arm64.whl", hash = "sha256:d4188e73af84ca82468f09cadc5ac4db578109e52acb4518d8154698d3a87ca2", size = 64995, upload-time = "2025-08-10T21:26:43.889Z" }, + { url = "https://files.pythonhosted.org/packages/e2/37/7d218ce5d92dadc5ebdd9070d903e0c7cf7edfe03f179433ac4d13ce659c/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:5a0f2724dfd4e3b3ac5a82436a8e6fd16baa7d507117e4279b660fe8ca38a3a1", size = 126510, upload-time = "2025-08-10T21:26:44.915Z" }, + { url = "https://files.pythonhosted.org/packages/23/b0/e85a2b48233daef4b648fb657ebbb6f8367696a2d9548a00b4ee0eb67803/kiwisolver-1.4.9-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:1b11d6a633e4ed84fc0ddafd4ebfd8ea49b3f25082c04ad12b8315c11d504dc1", size = 67903, upload-time = "2025-08-10T21:26:45.934Z" }, + { url = "https://files.pythonhosted.org/packages/44/98/f2425bc0113ad7de24da6bb4dae1343476e95e1d738be7c04d31a5d037fd/kiwisolver-1.4.9-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61874cdb0a36016354853593cffc38e56fc9ca5aa97d2c05d3dcf6922cd55a11", size = 66402, upload-time = "2025-08-10T21:26:47.101Z" }, + { url = "https://files.pythonhosted.org/packages/98/d8/594657886df9f34c4177cc353cc28ca7e6e5eb562d37ccc233bff43bbe2a/kiwisolver-1.4.9-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:60c439763a969a6af93b4881db0eed8fadf93ee98e18cbc35bc8da868d0c4f0c", size = 1582135, upload-time = "2025-08-10T21:26:48.665Z" }, + { url = "https://files.pythonhosted.org/packages/5c/c6/38a115b7170f8b306fc929e166340c24958347308ea3012c2b44e7e295db/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92a2f997387a1b79a75e7803aa7ded2cfbe2823852ccf1ba3bcf613b62ae3197", size = 1389409, upload-time = "2025-08-10T21:26:50.335Z" }, + { url = "https://files.pythonhosted.org/packages/bf/3b/e04883dace81f24a568bcee6eb3001da4ba05114afa622ec9b6fafdc1f5e/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a31d512c812daea6d8b3be3b2bfcbeb091dbb09177706569bcfc6240dcf8b41c", size = 1401763, upload-time = "2025-08-10T21:26:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/9f/80/20ace48e33408947af49d7d15c341eaee69e4e0304aab4b7660e234d6288/kiwisolver-1.4.9-cp313-cp313t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:52a15b0f35dad39862d376df10c5230155243a2c1a436e39eb55623ccbd68185", size = 1453643, upload-time = "2025-08-10T21:26:53.592Z" }, + { url = "https://files.pythonhosted.org/packages/64/31/6ce4380a4cd1f515bdda976a1e90e547ccd47b67a1546d63884463c92ca9/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a30fd6fdef1430fd9e1ba7b3398b5ee4e2887783917a687d86ba69985fb08748", size = 2330818, upload-time = "2025-08-10T21:26:55.051Z" }, + { url = "https://files.pythonhosted.org/packages/fa/e9/3f3fcba3bcc7432c795b82646306e822f3fd74df0ee81f0fa067a1f95668/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:cc9617b46837c6468197b5945e196ee9ca43057bb7d9d1ae688101e4e1dddf64", size = 2419963, upload-time = "2025-08-10T21:26:56.421Z" }, + { url = "https://files.pythonhosted.org/packages/99/43/7320c50e4133575c66e9f7dadead35ab22d7c012a3b09bb35647792b2a6d/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:0ab74e19f6a2b027ea4f845a78827969af45ce790e6cb3e1ebab71bdf9f215ff", size = 2594639, upload-time = "2025-08-10T21:26:57.882Z" }, + { url = "https://files.pythonhosted.org/packages/65/d6/17ae4a270d4a987ef8a385b906d2bdfc9fce502d6dc0d3aea865b47f548c/kiwisolver-1.4.9-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:dba5ee5d3981160c28d5490f0d1b7ed730c22470ff7f6cc26cfcfaacb9896a07", size = 2391741, upload-time = "2025-08-10T21:26:59.237Z" }, + { url = "https://files.pythonhosted.org/packages/2a/8f/8f6f491d595a9e5912971f3f863d81baddccc8a4d0c3749d6a0dd9ffc9df/kiwisolver-1.4.9-cp313-cp313t-win_arm64.whl", hash = "sha256:0749fd8f4218ad2e851e11cc4dc05c7cbc0cbc4267bdfdb31782e65aace4ee9c", size = 68646, upload-time = "2025-08-10T21:27:00.52Z" }, + { url = "https://files.pythonhosted.org/packages/6b/32/6cc0fbc9c54d06c2969faa9c1d29f5751a2e51809dd55c69055e62d9b426/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:9928fe1eb816d11ae170885a74d074f57af3a0d65777ca47e9aeb854a1fba386", size = 123806, upload-time = "2025-08-10T21:27:01.537Z" }, + { url = "https://files.pythonhosted.org/packages/b2/dd/2bfb1d4a4823d92e8cbb420fe024b8d2167f72079b3bb941207c42570bdf/kiwisolver-1.4.9-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d0005b053977e7b43388ddec89fa567f43d4f6d5c2c0affe57de5ebf290dc552", size = 66605, upload-time = "2025-08-10T21:27:03.335Z" }, + { url = "https://files.pythonhosted.org/packages/f7/69/00aafdb4e4509c2ca6064646cba9cd4b37933898f426756adb2cb92ebbed/kiwisolver-1.4.9-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:2635d352d67458b66fd0667c14cb1d4145e9560d503219034a18a87e971ce4f3", size = 64925, upload-time = "2025-08-10T21:27:04.339Z" }, + { url = "https://files.pythonhosted.org/packages/43/dc/51acc6791aa14e5cb6d8a2e28cefb0dc2886d8862795449d021334c0df20/kiwisolver-1.4.9-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:767c23ad1c58c9e827b649a9ab7809fd5fd9db266a9cf02b0e926ddc2c680d58", size = 1472414, upload-time = "2025-08-10T21:27:05.437Z" }, + { url = "https://files.pythonhosted.org/packages/3d/bb/93fa64a81db304ac8a246f834d5094fae4b13baf53c839d6bb6e81177129/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:72d0eb9fba308b8311685c2268cf7d0a0639a6cd027d8128659f72bdd8a024b4", size = 1281272, upload-time = "2025-08-10T21:27:07.063Z" }, + { url = "https://files.pythonhosted.org/packages/70/e6/6df102916960fb8d05069d4bd92d6d9a8202d5a3e2444494e7cd50f65b7a/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:f68e4f3eeca8fb22cc3d731f9715a13b652795ef657a13df1ad0c7dc0e9731df", size = 1298578, upload-time = "2025-08-10T21:27:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/7c/47/e142aaa612f5343736b087864dbaebc53ea8831453fb47e7521fa8658f30/kiwisolver-1.4.9-cp314-cp314-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d84cd4061ae292d8ac367b2c3fa3aad11cb8625a95d135fe93f286f914f3f5a6", size = 1345607, upload-time = "2025-08-10T21:27:10.125Z" }, + { url = "https://files.pythonhosted.org/packages/54/89/d641a746194a0f4d1a3670fb900d0dbaa786fb98341056814bc3f058fa52/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a60ea74330b91bd22a29638940d115df9dc00af5035a9a2a6ad9399ffb4ceca5", size = 2230150, upload-time = "2025-08-10T21:27:11.484Z" }, + { url = "https://files.pythonhosted.org/packages/aa/6b/5ee1207198febdf16ac11f78c5ae40861b809cbe0e6d2a8d5b0b3044b199/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ce6a3a4e106cf35c2d9c4fa17c05ce0b180db622736845d4315519397a77beaf", size = 2325979, upload-time = "2025-08-10T21:27:12.917Z" }, + { url = "https://files.pythonhosted.org/packages/fc/ff/b269eefd90f4ae14dcc74973d5a0f6d28d3b9bb1afd8c0340513afe6b39a/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:77937e5e2a38a7b48eef0585114fe7930346993a88060d0bf886086d2aa49ef5", size = 2491456, upload-time = "2025-08-10T21:27:14.353Z" }, + { url = "https://files.pythonhosted.org/packages/fc/d4/10303190bd4d30de547534601e259a4fbf014eed94aae3e5521129215086/kiwisolver-1.4.9-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:24c175051354f4a28c5d6a31c93906dc653e2bf234e8a4bbfb964892078898ce", size = 2294621, upload-time = "2025-08-10T21:27:15.808Z" }, + { url = "https://files.pythonhosted.org/packages/28/e0/a9a90416fce5c0be25742729c2ea52105d62eda6c4be4d803c2a7be1fa50/kiwisolver-1.4.9-cp314-cp314-win_amd64.whl", hash = "sha256:0763515d4df10edf6d06a3c19734e2566368980d21ebec439f33f9eb936c07b7", size = 75417, upload-time = "2025-08-10T21:27:17.436Z" }, + { url = "https://files.pythonhosted.org/packages/1f/10/6949958215b7a9a264299a7db195564e87900f709db9245e4ebdd3c70779/kiwisolver-1.4.9-cp314-cp314-win_arm64.whl", hash = "sha256:0e4e2bf29574a6a7b7f6cb5fa69293b9f96c928949ac4a53ba3f525dffb87f9c", size = 66582, upload-time = "2025-08-10T21:27:18.436Z" }, + { url = "https://files.pythonhosted.org/packages/ec/79/60e53067903d3bc5469b369fe0dfc6b3482e2133e85dae9daa9527535991/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:d976bbb382b202f71c67f77b0ac11244021cfa3f7dfd9e562eefcea2df711548", size = 126514, upload-time = "2025-08-10T21:27:19.465Z" }, + { url = "https://files.pythonhosted.org/packages/25/d1/4843d3e8d46b072c12a38c97c57fab4608d36e13fe47d47ee96b4d61ba6f/kiwisolver-1.4.9-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2489e4e5d7ef9a1c300a5e0196e43d9c739f066ef23270607d45aba368b91f2d", size = 67905, upload-time = "2025-08-10T21:27:20.51Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ae/29ffcbd239aea8b93108de1278271ae764dfc0d803a5693914975f200596/kiwisolver-1.4.9-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:e2ea9f7ab7fbf18fffb1b5434ce7c69a07582f7acc7717720f1d69f3e806f90c", size = 66399, upload-time = "2025-08-10T21:27:21.496Z" }, + { url = "https://files.pythonhosted.org/packages/a1/ae/d7ba902aa604152c2ceba5d352d7b62106bedbccc8e95c3934d94472bfa3/kiwisolver-1.4.9-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b34e51affded8faee0dfdb705416153819d8ea9250bbbf7ea1b249bdeb5f1122", size = 1582197, upload-time = "2025-08-10T21:27:22.604Z" }, + { url = "https://files.pythonhosted.org/packages/f2/41/27c70d427eddb8bc7e4f16420a20fefc6f480312122a59a959fdfe0445ad/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d8aacd3d4b33b772542b2e01beb50187536967b514b00003bdda7589722d2a64", size = 1390125, upload-time = "2025-08-10T21:27:24.036Z" }, + { url = "https://files.pythonhosted.org/packages/41/42/b3799a12bafc76d962ad69083f8b43b12bf4fe78b097b12e105d75c9b8f1/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:7cf974dd4e35fa315563ac99d6287a1024e4dc2077b8a7d7cd3d2fb65d283134", size = 1402612, upload-time = "2025-08-10T21:27:25.773Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b5/a210ea073ea1cfaca1bb5c55a62307d8252f531beb364e18aa1e0888b5a0/kiwisolver-1.4.9-cp314-cp314t-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:85bd218b5ecfbee8c8a82e121802dcb519a86044c9c3b2e4aef02fa05c6da370", size = 1453990, upload-time = "2025-08-10T21:27:27.089Z" }, + { url = "https://files.pythonhosted.org/packages/5f/ce/a829eb8c033e977d7ea03ed32fb3c1781b4fa0433fbadfff29e39c676f32/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:0856e241c2d3df4efef7c04a1e46b1936b6120c9bcf36dd216e3acd84bc4fb21", size = 2331601, upload-time = "2025-08-10T21:27:29.343Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/b5e97eb142eb9cd0072dacfcdcd31b1c66dc7352b0f7c7255d339c0edf00/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:9af39d6551f97d31a4deebeac6f45b156f9755ddc59c07b402c148f5dbb6482a", size = 2422041, upload-time = "2025-08-10T21:27:30.754Z" }, + { url = "https://files.pythonhosted.org/packages/40/be/8eb4cd53e1b85ba4edc3a9321666f12b83113a178845593307a3e7891f44/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:bb4ae2b57fc1d8cbd1cf7b1d9913803681ffa903e7488012be5b76dedf49297f", size = 2594897, upload-time = "2025-08-10T21:27:32.803Z" }, + { url = "https://files.pythonhosted.org/packages/99/dd/841e9a66c4715477ea0abc78da039832fbb09dac5c35c58dc4c41a407b8a/kiwisolver-1.4.9-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:aedff62918805fb62d43a4aa2ecd4482c380dc76cd31bd7c8878588a61bd0369", size = 2391835, upload-time = "2025-08-10T21:27:34.23Z" }, + { url = "https://files.pythonhosted.org/packages/0c/28/4b2e5c47a0da96896fdfdb006340ade064afa1e63675d01ea5ac222b6d52/kiwisolver-1.4.9-cp314-cp314t-win_amd64.whl", hash = "sha256:1fa333e8b2ce4d9660f2cda9c0e1b6bafcfb2457a9d259faa82289e73ec24891", size = 79988, upload-time = "2025-08-10T21:27:35.587Z" }, + { url = "https://files.pythonhosted.org/packages/80/be/3578e8afd18c88cdf9cb4cffde75a96d2be38c5a903f1ed0ceec061bd09e/kiwisolver-1.4.9-cp314-cp314t-win_arm64.whl", hash = "sha256:4a48a2ce79d65d363597ef7b567ce3d14d68783d2b2263d98db3d9477805ba32", size = 70260, upload-time = "2025-08-10T21:27:36.606Z" }, + { url = "https://files.pythonhosted.org/packages/a2/63/fde392691690f55b38d5dd7b3710f5353bf7a8e52de93a22968801ab8978/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4d1d9e582ad4d63062d34077a9a1e9f3c34088a2ec5135b1f7190c07cf366527", size = 60183, upload-time = "2025-08-10T21:27:37.669Z" }, + { url = "https://files.pythonhosted.org/packages/27/b1/6aad34edfdb7cced27f371866f211332bba215bfd918ad3322a58f480d8b/kiwisolver-1.4.9-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:deed0c7258ceb4c44ad5ec7d9918f9f14fd05b2be86378d86cf50e63d1e7b771", size = 58675, upload-time = "2025-08-10T21:27:39.031Z" }, + { url = "https://files.pythonhosted.org/packages/9d/1a/23d855a702bb35a76faed5ae2ba3de57d323f48b1f6b17ee2176c4849463/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0a590506f303f512dff6b7f75fd2fd18e16943efee932008fe7140e5fa91d80e", size = 80277, upload-time = "2025-08-10T21:27:40.129Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5b/5239e3c2b8fb5afa1e8508f721bb77325f740ab6994d963e61b2b7abcc1e/kiwisolver-1.4.9-pp310-pypy310_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e09c2279a4d01f099f52d5c4b3d9e208e91edcbd1a175c9662a8b16e000fece9", size = 77994, upload-time = "2025-08-10T21:27:41.181Z" }, + { url = "https://files.pythonhosted.org/packages/f9/1c/5d4d468fb16f8410e596ed0eac02d2c68752aa7dc92997fe9d60a7147665/kiwisolver-1.4.9-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:c9e7cdf45d594ee04d5be1b24dd9d49f3d1590959b2271fb30b5ca2b262c00fb", size = 73744, upload-time = "2025-08-10T21:27:42.254Z" }, + { url = "https://files.pythonhosted.org/packages/a3/0f/36d89194b5a32c054ce93e586d4049b6c2c22887b0eb229c61c68afd3078/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:720e05574713db64c356e86732c0f3c5252818d05f9df320f0ad8380641acea5", size = 60104, upload-time = "2025-08-10T21:27:43.287Z" }, + { url = "https://files.pythonhosted.org/packages/52/ba/4ed75f59e4658fd21fe7dde1fee0ac397c678ec3befba3fe6482d987af87/kiwisolver-1.4.9-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:17680d737d5335b552994a2008fab4c851bcd7de33094a82067ef3a576ff02fa", size = 58592, upload-time = "2025-08-10T21:27:44.314Z" }, + { url = "https://files.pythonhosted.org/packages/33/01/a8ea7c5ea32a9b45ceeaee051a04c8ed4320f5add3c51bfa20879b765b70/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:85b5352f94e490c028926ea567fc569c52ec79ce131dadb968d3853e809518c2", size = 80281, upload-time = "2025-08-10T21:27:45.369Z" }, + { url = "https://files.pythonhosted.org/packages/da/e3/dbd2ecdce306f1d07a1aaf324817ee993aab7aee9db47ceac757deabafbe/kiwisolver-1.4.9-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:464415881e4801295659462c49461a24fb107c140de781d55518c4b80cb6790f", size = 78009, upload-time = "2025-08-10T21:27:46.376Z" }, + { url = "https://files.pythonhosted.org/packages/da/e9/0d4add7873a73e462aeb45c036a2dead2562b825aa46ba326727b3f31016/kiwisolver-1.4.9-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:fb940820c63a9590d31d88b815e7a3aa5915cad3ce735ab45f0c730b39547de1", size = 73929, upload-time = "2025-08-10T21:27:48.236Z" }, +] + +[[package]] +name = "librt" +version = "0.8.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/56/9c/b4b0c54d84da4a94b37bd44151e46d5e583c9534c7e02250b961b1b6d8a8/librt-0.8.1.tar.gz", hash = "sha256:be46a14693955b3bd96014ccbdb8339ee8c9346fbe11c1b78901b55125f14c73", size = 177471, upload-time = "2026-02-17T16:13:06.101Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7c/5f/63f5fa395c7a8a93558c0904ba8f1c8d1b997ca6a3de61bc7659970d66bf/librt-0.8.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:81fd938344fecb9373ba1b155968c8a329491d2ce38e7ddb76f30ffb938f12dc", size = 65697, upload-time = "2026-02-17T16:11:06.903Z" }, + { url = "https://files.pythonhosted.org/packages/ff/e0/0472cf37267b5920eff2f292ccfaede1886288ce35b7f3203d8de00abfe6/librt-0.8.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5db05697c82b3a2ec53f6e72b2ed373132b0c2e05135f0696784e97d7f5d48e7", size = 68376, upload-time = "2026-02-17T16:11:08.395Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8bd1359fdcd27ab897cd5963294fa4a7c83b20a8564678e4fd12157e56a5/librt-0.8.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:d56bc4011975f7460bea7b33e1ff425d2f1adf419935ff6707273c77f8a4ada6", size = 197084, upload-time = "2026-02-17T16:11:09.774Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fe/163e33fdd091d0c2b102f8a60cc0a61fd730ad44e32617cd161e7cd67a01/librt-0.8.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5cdc0f588ff4b663ea96c26d2a230c525c6fc62b28314edaaaca8ed5af931ad0", size = 207337, upload-time = "2026-02-17T16:11:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/01/99/f85130582f05dcf0c8902f3d629270231d2f4afdfc567f8305a952ac7f14/librt-0.8.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:97c2b54ff6717a7a563b72627990bec60d8029df17df423f0ed37d56a17a176b", size = 219980, upload-time = "2026-02-17T16:11:12.499Z" }, + { url = "https://files.pythonhosted.org/packages/6f/54/cb5e4d03659e043a26c74e08206412ac9a3742f0477d96f9761a55313b5f/librt-0.8.1-cp310-cp310-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8f1125e6bbf2f1657d9a2f3ccc4a2c9b0c8b176965bb565dd4d86be67eddb4b6", size = 212921, upload-time = "2026-02-17T16:11:14.484Z" }, + { url = "https://files.pythonhosted.org/packages/b1/81/a3a01e4240579c30f3487f6fed01eb4bc8ef0616da5b4ebac27ca19775f3/librt-0.8.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:8f4bb453f408137d7581be309b2fbc6868a80e7ef60c88e689078ee3a296ae71", size = 221381, upload-time = "2026-02-17T16:11:17.459Z" }, + { url = "https://files.pythonhosted.org/packages/08/b0/fc2d54b4b1c6fb81e77288ff31ff25a2c1e62eaef4424a984f228839717b/librt-0.8.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:c336d61d2fe74a3195edc1646d53ff1cddd3a9600b09fa6ab75e5514ba4862a7", size = 216714, upload-time = "2026-02-17T16:11:19.197Z" }, + { url = "https://files.pythonhosted.org/packages/96/96/85daa73ffbd87e1fb287d7af6553ada66bf25a2a6b0de4764344a05469f6/librt-0.8.1-cp310-cp310-musllinux_1_2_riscv64.whl", hash = "sha256:eb5656019db7c4deacf0c1a55a898c5bb8f989be904597fcb5232a2f4828fa05", size = 214777, upload-time = "2026-02-17T16:11:20.443Z" }, + { url = "https://files.pythonhosted.org/packages/12/9c/c3aa7a2360383f4bf4f04d98195f2739a579128720c603f4807f006a4225/librt-0.8.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:c25d9e338d5bed46c1632f851babf3d13c78f49a225462017cf5e11e845c5891", size = 237398, upload-time = "2026-02-17T16:11:22.083Z" }, + { url = "https://files.pythonhosted.org/packages/61/19/d350ea89e5274665185dabc4bbb9c3536c3411f862881d316c8b8e00eb66/librt-0.8.1-cp310-cp310-win32.whl", hash = "sha256:aaab0e307e344cb28d800957ef3ec16605146ef0e59e059a60a176d19543d1b7", size = 54285, upload-time = "2026-02-17T16:11:23.27Z" }, + { url = "https://files.pythonhosted.org/packages/4f/d6/45d587d3d41c112e9543a0093d883eb57a24a03e41561c127818aa2a6bcc/librt-0.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:56e04c14b696300d47b3bc5f1d10a00e86ae978886d0cee14e5714fafb5df5d2", size = 61352, upload-time = "2026-02-17T16:11:24.207Z" }, + { url = "https://files.pythonhosted.org/packages/1d/01/0e748af5e4fee180cf7cd12bd12b0513ad23b045dccb2a83191bde82d168/librt-0.8.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:681dc2451d6d846794a828c16c22dc452d924e9f700a485b7ecb887a30aad1fd", size = 65315, upload-time = "2026-02-17T16:11:25.152Z" }, + { url = "https://files.pythonhosted.org/packages/9d/4d/7184806efda571887c798d573ca4134c80ac8642dcdd32f12c31b939c595/librt-0.8.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a3b4350b13cc0e6f5bec8fa7caf29a8fb8cdc051a3bae45cfbfd7ce64f009965", size = 68021, upload-time = "2026-02-17T16:11:26.129Z" }, + { url = "https://files.pythonhosted.org/packages/ae/88/c3c52d2a5d5101f28d3dc89298444626e7874aa904eed498464c2af17627/librt-0.8.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:ac1e7817fd0ed3d14fd7c5df91daed84c48e4c2a11ee99c0547f9f62fdae13da", size = 194500, upload-time = "2026-02-17T16:11:27.177Z" }, + { url = "https://files.pythonhosted.org/packages/d6/5d/6fb0a25b6a8906e85b2c3b87bee1d6ed31510be7605b06772f9374ca5cb3/librt-0.8.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:747328be0c5b7075cde86a0e09d7a9196029800ba75a1689332348e998fb85c0", size = 205622, upload-time = "2026-02-17T16:11:28.242Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a6/8006ae81227105476a45691f5831499e4d936b1c049b0c1feb17c11b02d1/librt-0.8.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f0af2bd2bc204fa27f3d6711d0f360e6b8c684a035206257a81673ab924aa11e", size = 218304, upload-time = "2026-02-17T16:11:29.344Z" }, + { url = "https://files.pythonhosted.org/packages/ee/19/60e07886ad16670aae57ef44dada41912c90906a6fe9f2b9abac21374748/librt-0.8.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:d480de377f5b687b6b1bc0c0407426da556e2a757633cc7e4d2e1a057aa688f3", size = 211493, upload-time = "2026-02-17T16:11:30.445Z" }, + { url = "https://files.pythonhosted.org/packages/9c/cf/f666c89d0e861d05600438213feeb818c7514d3315bae3648b1fc145d2b6/librt-0.8.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d0ee06b5b5291f609ddb37b9750985b27bc567791bc87c76a569b3feed8481ac", size = 219129, upload-time = "2026-02-17T16:11:32.021Z" }, + { url = "https://files.pythonhosted.org/packages/8f/ef/f1bea01e40b4a879364c031476c82a0dc69ce068daad67ab96302fed2d45/librt-0.8.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:9e2c6f77b9ad48ce5603b83b7da9ee3e36b3ab425353f695cba13200c5d96596", size = 213113, upload-time = "2026-02-17T16:11:33.192Z" }, + { url = "https://files.pythonhosted.org/packages/9b/80/cdab544370cc6bc1b72ea369525f547a59e6938ef6863a11ab3cd24759af/librt-0.8.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:439352ba9373f11cb8e1933da194dcc6206daf779ff8df0ed69c5e39113e6a99", size = 212269, upload-time = "2026-02-17T16:11:34.373Z" }, + { url = "https://files.pythonhosted.org/packages/9d/9c/48d6ed8dac595654f15eceab2035131c136d1ae9a1e3548e777bb6dbb95d/librt-0.8.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:82210adabbc331dbb65d7868b105185464ef13f56f7f76688565ad79f648b0fe", size = 234673, upload-time = "2026-02-17T16:11:36.063Z" }, + { url = "https://files.pythonhosted.org/packages/16/01/35b68b1db517f27a01be4467593292eb5315def8900afad29fabf56304ba/librt-0.8.1-cp311-cp311-win32.whl", hash = "sha256:52c224e14614b750c0a6d97368e16804a98c684657c7518752c356834fff83bb", size = 54597, upload-time = "2026-02-17T16:11:37.544Z" }, + { url = "https://files.pythonhosted.org/packages/71/02/796fe8f02822235966693f257bf2c79f40e11337337a657a8cfebba5febc/librt-0.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:c00e5c884f528c9932d278d5c9cbbea38a6b81eb62c02e06ae53751a83a4d52b", size = 61733, upload-time = "2026-02-17T16:11:38.691Z" }, + { url = "https://files.pythonhosted.org/packages/28/ad/232e13d61f879a42a4e7117d65e4984bb28371a34bb6fb9ca54ec2c8f54e/librt-0.8.1-cp311-cp311-win_arm64.whl", hash = "sha256:f7cdf7f26c2286ffb02e46d7bac56c94655540b26347673bea15fa52a6af17e9", size = 52273, upload-time = "2026-02-17T16:11:40.308Z" }, + { url = "https://files.pythonhosted.org/packages/95/21/d39b0a87ac52fc98f621fb6f8060efb017a767ebbbac2f99fbcbc9ddc0d7/librt-0.8.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a28f2612ab566b17f3698b0da021ff9960610301607c9a5e8eaca62f5e1c350a", size = 66516, upload-time = "2026-02-17T16:11:41.604Z" }, + { url = "https://files.pythonhosted.org/packages/69/f1/46375e71441c43e8ae335905e069f1c54febee63a146278bcee8782c84fd/librt-0.8.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:60a78b694c9aee2a0f1aaeaa7d101cf713e92e8423a941d2897f4fa37908dab9", size = 68634, upload-time = "2026-02-17T16:11:43.268Z" }, + { url = "https://files.pythonhosted.org/packages/0a/33/c510de7f93bf1fa19e13423a606d8189a02624a800710f6e6a0a0f0784b3/librt-0.8.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:758509ea3f1eba2a57558e7e98f4659d0ea7670bff49673b0dde18a3c7e6c0eb", size = 198941, upload-time = "2026-02-17T16:11:44.28Z" }, + { url = "https://files.pythonhosted.org/packages/dd/36/e725903416409a533d92398e88ce665476f275081d0d7d42f9c4951999e5/librt-0.8.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:039b9f2c506bd0ab0f8725aa5ba339c6f0cd19d3b514b50d134789809c24285d", size = 209991, upload-time = "2026-02-17T16:11:45.462Z" }, + { url = "https://files.pythonhosted.org/packages/30/7a/8d908a152e1875c9f8eac96c97a480df425e657cdb47854b9efaa4998889/librt-0.8.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5bb54f1205a3a6ab41a6fd71dfcdcbd278670d3a90ca502a30d9da583105b6f7", size = 224476, upload-time = "2026-02-17T16:11:46.542Z" }, + { url = "https://files.pythonhosted.org/packages/a8/b8/a22c34f2c485b8903a06f3fe3315341fe6876ef3599792344669db98fcff/librt-0.8.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:05bd41cdee35b0c59c259f870f6da532a2c5ca57db95b5f23689fcb5c9e42440", size = 217518, upload-time = "2026-02-17T16:11:47.746Z" }, + { url = "https://files.pythonhosted.org/packages/79/6f/5c6fea00357e4f82ba44f81dbfb027921f1ab10e320d4a64e1c408d035d9/librt-0.8.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adfab487facf03f0d0857b8710cf82d0704a309d8ffc33b03d9302b4c64e91a9", size = 225116, upload-time = "2026-02-17T16:11:49.298Z" }, + { url = "https://files.pythonhosted.org/packages/f2/a0/95ced4e7b1267fe1e2720a111685bcddf0e781f7e9e0ce59d751c44dcfe5/librt-0.8.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:153188fe98a72f206042be10a2c6026139852805215ed9539186312d50a8e972", size = 217751, upload-time = "2026-02-17T16:11:50.49Z" }, + { url = "https://files.pythonhosted.org/packages/93/c2/0517281cb4d4101c27ab59472924e67f55e375bc46bedae94ac6dc6e1902/librt-0.8.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:dd3c41254ee98604b08bd5b3af5bf0a89740d4ee0711de95b65166bf44091921", size = 218378, upload-time = "2026-02-17T16:11:51.783Z" }, + { url = "https://files.pythonhosted.org/packages/43/e8/37b3ac108e8976888e559a7b227d0ceac03c384cfd3e7a1c2ee248dbae79/librt-0.8.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e0d138c7ae532908cbb342162b2611dbd4d90c941cd25ab82084aaf71d2c0bd0", size = 241199, upload-time = "2026-02-17T16:11:53.561Z" }, + { url = "https://files.pythonhosted.org/packages/4b/5b/35812d041c53967fedf551a39399271bbe4257e681236a2cf1a69c8e7fa1/librt-0.8.1-cp312-cp312-win32.whl", hash = "sha256:43353b943613c5d9c49a25aaffdba46f888ec354e71e3529a00cca3f04d66a7a", size = 54917, upload-time = "2026-02-17T16:11:54.758Z" }, + { url = "https://files.pythonhosted.org/packages/de/d1/fa5d5331b862b9775aaf2a100f5ef86854e5d4407f71bddf102f4421e034/librt-0.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:ff8baf1f8d3f4b6b7257fcb75a501f2a5499d0dda57645baa09d4d0d34b19444", size = 62017, upload-time = "2026-02-17T16:11:55.748Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7c/c614252f9acda59b01a66e2ddfd243ed1c7e1deab0293332dfbccf862808/librt-0.8.1-cp312-cp312-win_arm64.whl", hash = "sha256:0f2ae3725904f7377e11cc37722d5d401e8b3d5851fb9273d7f4fe04f6b3d37d", size = 52441, upload-time = "2026-02-17T16:11:56.801Z" }, + { url = "https://files.pythonhosted.org/packages/c5/3c/f614c8e4eaac7cbf2bbdf9528790b21d89e277ee20d57dc6e559c626105f/librt-0.8.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:7e6bad1cd94f6764e1e21950542f818a09316645337fd5ab9a7acc45d99a8f35", size = 66529, upload-time = "2026-02-17T16:11:57.809Z" }, + { url = "https://files.pythonhosted.org/packages/ab/96/5836544a45100ae411eda07d29e3d99448e5258b6e9c8059deb92945f5c2/librt-0.8.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cf450f498c30af55551ba4f66b9123b7185362ec8b625a773b3d39aa1a717583", size = 68669, upload-time = "2026-02-17T16:11:58.843Z" }, + { url = "https://files.pythonhosted.org/packages/06/53/f0b992b57af6d5531bf4677d75c44f095f2366a1741fb695ee462ae04b05/librt-0.8.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:eca45e982fa074090057132e30585a7e8674e9e885d402eae85633e9f449ce6c", size = 199279, upload-time = "2026-02-17T16:11:59.862Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ad/4848cc16e268d14280d8168aee4f31cea92bbd2b79ce33d3e166f2b4e4fc/librt-0.8.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0c3811485fccfda840861905b8c70bba5ec094e02825598bb9d4ca3936857a04", size = 210288, upload-time = "2026-02-17T16:12:00.954Z" }, + { url = "https://files.pythonhosted.org/packages/52/05/27fdc2e95de26273d83b96742d8d3b7345f2ea2bdbd2405cc504644f2096/librt-0.8.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e4af413908f77294605e28cfd98063f54b2c790561383971d2f52d113d9c363", size = 224809, upload-time = "2026-02-17T16:12:02.108Z" }, + { url = "https://files.pythonhosted.org/packages/7a/d0/78200a45ba3240cb042bc597d6f2accba9193a2c57d0356268cbbe2d0925/librt-0.8.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5212a5bd7fae98dae95710032902edcd2ec4dc994e883294f75c857b83f9aba0", size = 218075, upload-time = "2026-02-17T16:12:03.631Z" }, + { url = "https://files.pythonhosted.org/packages/af/72/a210839fa74c90474897124c064ffca07f8d4b347b6574d309686aae7ca6/librt-0.8.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e692aa2d1d604e6ca12d35e51fdc36f4cda6345e28e36374579f7ef3611b3012", size = 225486, upload-time = "2026-02-17T16:12:04.725Z" }, + { url = "https://files.pythonhosted.org/packages/a3/c1/a03cc63722339ddbf087485f253493e2b013039f5b707e8e6016141130fa/librt-0.8.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:4be2a5c926b9770c9e08e717f05737a269b9d0ebc5d2f0060f0fe3fe9ce47acb", size = 218219, upload-time = "2026-02-17T16:12:05.828Z" }, + { url = "https://files.pythonhosted.org/packages/58/f5/fff6108af0acf941c6f274a946aea0e484bd10cd2dc37610287ce49388c5/librt-0.8.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd1a720332ea335ceb544cf0a03f81df92abd4bb887679fd1e460976b0e6214b", size = 218750, upload-time = "2026-02-17T16:12:07.09Z" }, + { url = "https://files.pythonhosted.org/packages/71/67/5a387bfef30ec1e4b4f30562c8586566faf87e47d696768c19feb49e3646/librt-0.8.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:93c2af9e01e0ef80d95ae3c720be101227edae5f2fe7e3dc63d8857fadfc5a1d", size = 241624, upload-time = "2026-02-17T16:12:08.43Z" }, + { url = "https://files.pythonhosted.org/packages/d4/be/24f8502db11d405232ac1162eb98069ca49c3306c1d75c6ccc61d9af8789/librt-0.8.1-cp313-cp313-win32.whl", hash = "sha256:086a32dbb71336627e78cc1d6ee305a68d038ef7d4c39aaff41ae8c9aa46e91a", size = 54969, upload-time = "2026-02-17T16:12:09.633Z" }, + { url = "https://files.pythonhosted.org/packages/5c/73/c9fdf6cb2a529c1a092ce769a12d88c8cca991194dfe641b6af12fa964d2/librt-0.8.1-cp313-cp313-win_amd64.whl", hash = "sha256:e11769a1dbda4da7b00a76cfffa67aa47cfa66921d2724539eee4b9ede780b79", size = 62000, upload-time = "2026-02-17T16:12:10.632Z" }, + { url = "https://files.pythonhosted.org/packages/d3/97/68f80ca3ac4924f250cdfa6e20142a803e5e50fca96ef5148c52ee8c10ea/librt-0.8.1-cp313-cp313-win_arm64.whl", hash = "sha256:924817ab3141aca17893386ee13261f1d100d1ef410d70afe4389f2359fea4f0", size = 52495, upload-time = "2026-02-17T16:12:11.633Z" }, + { url = "https://files.pythonhosted.org/packages/c9/6a/907ef6800f7bca71b525a05f1839b21f708c09043b1c6aa77b6b827b3996/librt-0.8.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:6cfa7fe54fd4d1f47130017351a959fe5804bda7a0bc7e07a2cdbc3fdd28d34f", size = 66081, upload-time = "2026-02-17T16:12:12.766Z" }, + { url = "https://files.pythonhosted.org/packages/1b/18/25e991cd5640c9fb0f8d91b18797b29066b792f17bf8493da183bf5caabe/librt-0.8.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:228c2409c079f8c11fb2e5d7b277077f694cb93443eb760e00b3b83cb8b3176c", size = 68309, upload-time = "2026-02-17T16:12:13.756Z" }, + { url = "https://files.pythonhosted.org/packages/a4/36/46820d03f058cfb5a9de5940640ba03165ed8aded69e0733c417bb04df34/librt-0.8.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:7aae78ab5e3206181780e56912d1b9bb9f90a7249ce12f0e8bf531d0462dd0fc", size = 196804, upload-time = "2026-02-17T16:12:14.818Z" }, + { url = "https://files.pythonhosted.org/packages/59/18/5dd0d3b87b8ff9c061849fbdb347758d1f724b9a82241aa908e0ec54ccd0/librt-0.8.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:172d57ec04346b047ca6af181e1ea4858086c80bdf455f61994c4aa6fc3f866c", size = 206907, upload-time = "2026-02-17T16:12:16.513Z" }, + { url = "https://files.pythonhosted.org/packages/d1/96/ef04902aad1424fd7299b62d1890e803e6ab4018c3044dca5922319c4b97/librt-0.8.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6b1977c4ea97ce5eb7755a78fae68d87e4102e4aaf54985e8b56806849cc06a3", size = 221217, upload-time = "2026-02-17T16:12:17.906Z" }, + { url = "https://files.pythonhosted.org/packages/6d/ff/7e01f2dda84a8f5d280637a2e5827210a8acca9a567a54507ef1c75b342d/librt-0.8.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:10c42e1f6fd06733ef65ae7bebce2872bcafd8d6e6b0a08fe0a05a23b044fb14", size = 214622, upload-time = "2026-02-17T16:12:19.108Z" }, + { url = "https://files.pythonhosted.org/packages/1e/8c/5b093d08a13946034fed57619742f790faf77058558b14ca36a6e331161e/librt-0.8.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4c8dfa264b9193c4ee19113c985c95f876fae5e51f731494fc4e0cf594990ba7", size = 221987, upload-time = "2026-02-17T16:12:20.331Z" }, + { url = "https://files.pythonhosted.org/packages/d3/cc/86b0b3b151d40920ad45a94ce0171dec1aebba8a9d72bb3fa00c73ab25dd/librt-0.8.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:01170b6729a438f0dedc4a26ed342e3dc4f02d1000b4b19f980e1877f0c297e6", size = 215132, upload-time = "2026-02-17T16:12:21.54Z" }, + { url = "https://files.pythonhosted.org/packages/fc/be/8588164a46edf1e69858d952654e216a9a91174688eeefb9efbb38a9c799/librt-0.8.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7b02679a0d783bdae30d443025b94465d8c3dc512f32f5b5031f93f57ac32071", size = 215195, upload-time = "2026-02-17T16:12:23.073Z" }, + { url = "https://files.pythonhosted.org/packages/f5/f2/0b9279bea735c734d69344ecfe056c1ba211694a72df10f568745c899c76/librt-0.8.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:190b109bb69592a3401fe1ffdea41a2e73370ace2ffdc4a0e8e2b39cdea81b78", size = 237946, upload-time = "2026-02-17T16:12:24.275Z" }, + { url = "https://files.pythonhosted.org/packages/e9/cc/5f2a34fbc8aeb35314a3641f9956fa9051a947424652fad9882be7a97949/librt-0.8.1-cp314-cp314-win32.whl", hash = "sha256:e70a57ecf89a0f64c24e37f38d3fe217a58169d2fe6ed6d70554964042474023", size = 50689, upload-time = "2026-02-17T16:12:25.766Z" }, + { url = "https://files.pythonhosted.org/packages/a0/76/cd4d010ab2147339ca2b93e959c3686e964edc6de66ddacc935c325883d7/librt-0.8.1-cp314-cp314-win_amd64.whl", hash = "sha256:7e2f3edca35664499fbb36e4770650c4bd4a08abc1f4458eab9df4ec56389730", size = 57875, upload-time = "2026-02-17T16:12:27.465Z" }, + { url = "https://files.pythonhosted.org/packages/84/0f/2143cb3c3ca48bd3379dcd11817163ca50781927c4537345d608b5045998/librt-0.8.1-cp314-cp314-win_arm64.whl", hash = "sha256:0d2f82168e55ddefd27c01c654ce52379c0750ddc31ee86b4b266bcf4d65f2a3", size = 48058, upload-time = "2026-02-17T16:12:28.556Z" }, + { url = "https://files.pythonhosted.org/packages/d2/0e/9b23a87e37baf00311c3efe6b48d6b6c168c29902dfc3f04c338372fd7db/librt-0.8.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:2c74a2da57a094bd48d03fa5d196da83d2815678385d2978657499063709abe1", size = 68313, upload-time = "2026-02-17T16:12:29.659Z" }, + { url = "https://files.pythonhosted.org/packages/db/9a/859c41e5a4f1c84200a7d2b92f586aa27133c8243b6cac9926f6e54d01b9/librt-0.8.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:a355d99c4c0d8e5b770313b8b247411ed40949ca44e33e46a4789b9293a907ee", size = 70994, upload-time = "2026-02-17T16:12:31.516Z" }, + { url = "https://files.pythonhosted.org/packages/4c/28/10605366ee599ed34223ac2bf66404c6fb59399f47108215d16d5ad751a8/librt-0.8.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:2eb345e8b33fb748227409c9f1233d4df354d6e54091f0e8fc53acdb2ffedeb7", size = 220770, upload-time = "2026-02-17T16:12:33.294Z" }, + { url = "https://files.pythonhosted.org/packages/af/8d/16ed8fd452dafae9c48d17a6bc1ee3e818fd40ef718d149a8eff2c9f4ea2/librt-0.8.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9be2f15e53ce4e83cc08adc29b26fb5978db62ef2a366fbdf716c8a6c8901040", size = 235409, upload-time = "2026-02-17T16:12:35.443Z" }, + { url = "https://files.pythonhosted.org/packages/89/1b/7bdf3e49349c134b25db816e4a3db6b94a47ac69d7d46b1e682c2c4949be/librt-0.8.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:785ae29c1f5c6e7c2cde2c7c0e148147f4503da3abc5d44d482068da5322fd9e", size = 246473, upload-time = "2026-02-17T16:12:36.656Z" }, + { url = "https://files.pythonhosted.org/packages/4e/8a/91fab8e4fd2a24930a17188c7af5380eb27b203d72101c9cc000dbdfd95a/librt-0.8.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1d3a7da44baf692f0c6aeb5b2a09c5e6fc7a703bca9ffa337ddd2e2da53f7732", size = 238866, upload-time = "2026-02-17T16:12:37.849Z" }, + { url = "https://files.pythonhosted.org/packages/b9/e0/c45a098843fc7c07e18a7f8a24ca8496aecbf7bdcd54980c6ca1aaa79a8e/librt-0.8.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5fc48998000cbc39ec0d5311312dda93ecf92b39aaf184c5e817d5d440b29624", size = 250248, upload-time = "2026-02-17T16:12:39.445Z" }, + { url = "https://files.pythonhosted.org/packages/82/30/07627de23036640c952cce0c1fe78972e77d7d2f8fd54fa5ef4554ff4a56/librt-0.8.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:e96baa6820280077a78244b2e06e416480ed859bbd8e5d641cf5742919d8beb4", size = 240629, upload-time = "2026-02-17T16:12:40.889Z" }, + { url = "https://files.pythonhosted.org/packages/fb/c1/55bfe1ee3542eba055616f9098eaf6eddb966efb0ca0f44eaa4aba327307/librt-0.8.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:31362dbfe297b23590530007062c32c6f6176f6099646bb2c95ab1b00a57c382", size = 239615, upload-time = "2026-02-17T16:12:42.446Z" }, + { url = "https://files.pythonhosted.org/packages/2b/39/191d3d28abc26c9099b19852e6c99f7f6d400b82fa5a4e80291bd3803e19/librt-0.8.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:cc3656283d11540ab0ea01978378e73e10002145117055e03722417aeab30994", size = 263001, upload-time = "2026-02-17T16:12:43.627Z" }, + { url = "https://files.pythonhosted.org/packages/b9/eb/7697f60fbe7042ab4e88f4ee6af496b7f222fffb0a4e3593ef1f29f81652/librt-0.8.1-cp314-cp314t-win32.whl", hash = "sha256:738f08021b3142c2918c03692608baed43bc51144c29e35807682f8070ee2a3a", size = 51328, upload-time = "2026-02-17T16:12:45.148Z" }, + { url = "https://files.pythonhosted.org/packages/7c/72/34bf2eb7a15414a23e5e70ecb9440c1d3179f393d9349338a91e2781c0fb/librt-0.8.1-cp314-cp314t-win_amd64.whl", hash = "sha256:89815a22daf9c51884fb5dbe4f1ef65ee6a146e0b6a8df05f753e2e4a9359bf4", size = 58722, upload-time = "2026-02-17T16:12:46.85Z" }, + { url = "https://files.pythonhosted.org/packages/b2/c8/d148e041732d631fc76036f8b30fae4e77b027a1e95b7a84bb522481a940/librt-0.8.1-cp314-cp314t-win_arm64.whl", hash = "sha256:bf512a71a23504ed08103a13c941f763db13fb11177beb3d9244c98c29fb4a61", size = 48755, upload-time = "2026-02-17T16:12:47.943Z" }, + { url = "https://files.pythonhosted.org/packages/01/1f/c7d8b66a3ca3ca3ed8ded4b32c96ee58a45920ebbbaa934355c74adcc33e/librt-0.8.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3dff3d3ca8db20e783b1bc7de49c0a2ab0b8387f31236d6a026597d07fcd68ac", size = 65990, upload-time = "2026-02-17T16:12:48.972Z" }, + { url = "https://files.pythonhosted.org/packages/56/be/ee9ba1730052313d08457f19beaa1b878619978863fba09b40aed5b5c123/librt-0.8.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:08eec3a1fc435f0d09c87b6bf1ec798986a3544f446b864e4099633a56fcd9ed", size = 68640, upload-time = "2026-02-17T16:12:50.24Z" }, + { url = "https://files.pythonhosted.org/packages/81/27/b7309298b96f7690cec3ceee38004c1a7f60fcd96d952d3ac344a1e3e8b3/librt-0.8.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:e3f0a41487fd5fad7e760b9e8a90e251e27c2816fbc2cff36a22a0e6bcbbd9dd", size = 196099, upload-time = "2026-02-17T16:12:52.788Z" }, + { url = "https://files.pythonhosted.org/packages/10/48/160a5aacdcb21824b10a52378c39e88c46a29bb31efdaf3910dd1f9b670e/librt-0.8.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bacdb58d9939d95cc557b4dbaa86527c9db2ac1ed76a18bc8d26f6dc8647d851", size = 206663, upload-time = "2026-02-17T16:12:55.017Z" }, + { url = "https://files.pythonhosted.org/packages/ee/65/33dd1d8caabb7c6805d87d095b143417dc96b0277c06ffa0508361422c82/librt-0.8.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b6d7ab1f01aa753188605b09a51faa44a3327400b00b8cce424c71910fc0a128", size = 219318, upload-time = "2026-02-17T16:12:56.145Z" }, + { url = "https://files.pythonhosted.org/packages/09/d4/353805aa6181c7950a2462bd6e855366eeca21a501f375228d72a51547df/librt-0.8.1-cp39-cp39-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:4998009e7cb9e896569f4be7004f09d0ed70d386fa99d42b6d363f6d200501ac", size = 212191, upload-time = "2026-02-17T16:12:57.326Z" }, + { url = "https://files.pythonhosted.org/packages/06/08/725b3f304d61eba56c713c251fb833a06d84bf93381caad5152366f5d2bb/librt-0.8.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2cc68eeeef5e906839c7bb0815748b5b0a974ec27125beefc0f942715785b551", size = 220672, upload-time = "2026-02-17T16:12:58.497Z" }, + { url = "https://files.pythonhosted.org/packages/0e/55/e8cdf04145872b3b97cb9b68287b22d1c08348227063f305aec11a3e6ce7/librt-0.8.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:0bf69d79a23f4f40b8673a947a234baeeb133b5078b483b7297c5916539cf5d5", size = 216172, upload-time = "2026-02-17T16:12:59.751Z" }, + { url = "https://files.pythonhosted.org/packages/8f/d8/23b1c6592d2422dd6829c672f45b1f1c257f219926b0d216fedb572d0184/librt-0.8.1-cp39-cp39-musllinux_1_2_riscv64.whl", hash = "sha256:22b46eabd76c1986ee7d231b0765ad387d7673bbd996aa0d0d054b38ac65d8f6", size = 214116, upload-time = "2026-02-17T16:13:01.056Z" }, + { url = "https://files.pythonhosted.org/packages/c9/92/2b44fd3cc3313f44e43bdbb41343735b568fa675fa351642b408ee48d418/librt-0.8.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:237796479f4d0637d6b9cbcb926ff424a97735e68ade6facf402df4ec93375ed", size = 236664, upload-time = "2026-02-17T16:13:02.314Z" }, + { url = "https://files.pythonhosted.org/packages/00/23/92313ecdab80e142d8ea10e8dfa6297694359dbaacc9e81679bdc8cbceb6/librt-0.8.1-cp39-cp39-win32.whl", hash = "sha256:4beb04b8c66c6ae62f8c1e0b2f097c1ebad9295c929a8d5286c05eae7c2fc7dc", size = 54368, upload-time = "2026-02-17T16:13:03.549Z" }, + { url = "https://files.pythonhosted.org/packages/68/36/18f6e768afad6b55a690d38427c53251b69b7ba8795512730fd2508b31a9/librt-0.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:64548cde61b692dc0dc379f4b5f59a2f582c2ebe7890d09c1ae3b9e66fa015b7", size = 61507, upload-time = "2026-02-17T16:13:04.556Z" }, +] + +[[package]] +name = "livekit" +source = { editable = "livekit-rtc" } +dependencies = [ + { name = "aiofiles" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "protobuf", version = "6.33.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20260221", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiofiles", specifier = ">=24" }, + { name = "numpy", specifier = ">=1.26" }, + { name = "protobuf", specifier = ">=5" }, + { name = "types-protobuf", specifier = ">=5" }, +] + +[[package]] +name = "livekit-api" +source = { editable = "livekit-api" } +dependencies = [ + { name = "aiohttp", version = "3.13.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "aiohttp", version = "3.14.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "livekit-protocol" }, + { name = "protobuf", version = "6.33.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyjwt" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20260221", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "aiohttp", specifier = ">=3.9.0" }, + { name = "livekit-protocol", editable = "livekit-protocol" }, + { name = "protobuf", specifier = ">=4" }, + { name = "pyjwt", specifier = ">=2.0.0" }, + { name = "types-protobuf", specifier = ">=4" }, +] + +[[package]] +name = "livekit-protocol" +source = { editable = "livekit-protocol" } +dependencies = [ + { name = "protobuf", version = "6.33.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "protobuf", version = "7.34.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20251210", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "types-protobuf", version = "6.32.1.20260221", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[package.metadata] +requires-dist = [ + { name = "protobuf", specifier = ">=5" }, + { name = "types-protobuf", specifier = ">=5" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "mdurl", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.9.4" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "contourpy", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "cycler", marker = "python_full_version < '3.10'" }, + { name = "fonttools", version = "4.60.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "importlib-resources", marker = "python_full_version < '3.10'" }, + { name = "kiwisolver", version = "1.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pillow", version = "11.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pyparsing", marker = "python_full_version < '3.10'" }, + { name = "python-dateutil", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/17/1747b4154034befd0ed33b52538f5eb7752d05bb51c5e2a31470c3bc7d52/matplotlib-3.9.4.tar.gz", hash = "sha256:1e00e8be7393cbdc6fedfa8a6fba02cf3e83814b285db1c60b906a023ba41bc3", size = 36106529, upload-time = "2024-12-13T05:56:34.184Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7e/94/27d2e2c30d54b56c7b764acc1874a909e34d1965a427fc7092bb6a588b63/matplotlib-3.9.4-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:c5fdd7abfb706dfa8d307af64a87f1a862879ec3cd8d0ec8637458f0885b9c50", size = 7885089, upload-time = "2024-12-13T05:54:24.224Z" }, + { url = "https://files.pythonhosted.org/packages/c6/25/828273307e40a68eb8e9df832b6b2aaad075864fdc1de4b1b81e40b09e48/matplotlib-3.9.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d89bc4e85e40a71d1477780366c27fb7c6494d293e1617788986f74e2a03d7ff", size = 7770600, upload-time = "2024-12-13T05:54:27.214Z" }, + { url = "https://files.pythonhosted.org/packages/f2/65/f841a422ec994da5123368d76b126acf4fc02ea7459b6e37c4891b555b83/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ddf9f3c26aae695c5daafbf6b94e4c1a30d6cd617ba594bbbded3b33a1fcfa26", size = 8200138, upload-time = "2024-12-13T05:54:29.497Z" }, + { url = "https://files.pythonhosted.org/packages/07/06/272aca07a38804d93b6050813de41ca7ab0e29ba7a9dd098e12037c919a9/matplotlib-3.9.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18ebcf248030173b59a868fda1fe42397253f6698995b55e81e1f57431d85e50", size = 8312711, upload-time = "2024-12-13T05:54:34.396Z" }, + { url = "https://files.pythonhosted.org/packages/98/37/f13e23b233c526b7e27ad61be0a771894a079e0f7494a10d8d81557e0e9a/matplotlib-3.9.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:974896ec43c672ec23f3f8c648981e8bc880ee163146e0312a9b8def2fac66f5", size = 9090622, upload-time = "2024-12-13T05:54:36.808Z" }, + { url = "https://files.pythonhosted.org/packages/4f/8c/b1f5bd2bd70e60f93b1b54c4d5ba7a992312021d0ddddf572f9a1a6d9348/matplotlib-3.9.4-cp310-cp310-win_amd64.whl", hash = "sha256:4598c394ae9711cec135639374e70871fa36b56afae17bdf032a345be552a88d", size = 7828211, upload-time = "2024-12-13T05:54:40.596Z" }, + { url = "https://files.pythonhosted.org/packages/74/4b/65be7959a8fa118a3929b49a842de5b78bb55475236fcf64f3e308ff74a0/matplotlib-3.9.4-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:d4dd29641d9fb8bc4492420c5480398dd40a09afd73aebe4eb9d0071a05fbe0c", size = 7894430, upload-time = "2024-12-13T05:54:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/e9/18/80f70d91896e0a517b4a051c3fd540daa131630fd75e02e250365353b253/matplotlib-3.9.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30e5b22e8bcfb95442bf7d48b0d7f3bdf4a450cbf68986ea45fca3d11ae9d099", size = 7780045, upload-time = "2024-12-13T05:54:46.414Z" }, + { url = "https://files.pythonhosted.org/packages/a2/73/ccb381026e3238c5c25c3609ba4157b2d1a617ec98d65a8b4ee4e1e74d02/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2bb0030d1d447fd56dcc23b4c64a26e44e898f0416276cac1ebc25522e0ac249", size = 8209906, upload-time = "2024-12-13T05:54:49.459Z" }, + { url = "https://files.pythonhosted.org/packages/ab/33/1648da77b74741c89f5ea95cbf42a291b4b364f2660b316318811404ed97/matplotlib-3.9.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aca90ed222ac3565d2752b83dbb27627480d27662671e4d39da72e97f657a423", size = 8322873, upload-time = "2024-12-13T05:54:53.066Z" }, + { url = "https://files.pythonhosted.org/packages/57/d3/8447ba78bc6593c9044c372d1609f8ea10fb1e071e7a9e0747bea74fc16c/matplotlib-3.9.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a181b2aa2906c608fcae72f977a4a2d76e385578939891b91c2550c39ecf361e", size = 9099566, upload-time = "2024-12-13T05:54:55.522Z" }, + { url = "https://files.pythonhosted.org/packages/23/e1/4f0e237bf349c02ff9d1b6e7109f1a17f745263809b9714a8576dc17752b/matplotlib-3.9.4-cp311-cp311-win_amd64.whl", hash = "sha256:1f6882828231eca17f501c4dcd98a05abb3f03d157fbc0769c6911fe08b6cfd3", size = 7838065, upload-time = "2024-12-13T05:54:58.337Z" }, + { url = "https://files.pythonhosted.org/packages/1a/2b/c918bf6c19d6445d1cefe3d2e42cb740fb997e14ab19d4daeb6a7ab8a157/matplotlib-3.9.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:dfc48d67e6661378a21c2983200a654b72b5c5cdbd5d2cf6e5e1ece860f0cc70", size = 7891131, upload-time = "2024-12-13T05:55:02.837Z" }, + { url = "https://files.pythonhosted.org/packages/c1/e5/b4e8fc601ca302afeeabf45f30e706a445c7979a180e3a978b78b2b681a4/matplotlib-3.9.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:47aef0fab8332d02d68e786eba8113ffd6f862182ea2999379dec9e237b7e483", size = 7776365, upload-time = "2024-12-13T05:55:05.158Z" }, + { url = "https://files.pythonhosted.org/packages/99/06/b991886c506506476e5d83625c5970c656a491b9f80161458fed94597808/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fba1f52c6b7dc764097f52fd9ab627b90db452c9feb653a59945de16752e965f", size = 8200707, upload-time = "2024-12-13T05:55:09.48Z" }, + { url = "https://files.pythonhosted.org/packages/c3/e2/556b627498cb27e61026f2d1ba86a78ad1b836fef0996bef5440e8bc9559/matplotlib-3.9.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:173ac3748acaac21afcc3fa1633924609ba1b87749006bc25051c52c422a5d00", size = 8313761, upload-time = "2024-12-13T05:55:12.95Z" }, + { url = "https://files.pythonhosted.org/packages/58/ff/165af33ec766ff818306ea88e91f9f60d2a6ed543be1eb122a98acbf3b0d/matplotlib-3.9.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:320edea0cadc07007765e33f878b13b3738ffa9745c5f707705692df70ffe0e0", size = 9095284, upload-time = "2024-12-13T05:55:16.199Z" }, + { url = "https://files.pythonhosted.org/packages/9f/8b/3d0c7a002db3b1ed702731c2a9a06d78d035f1f2fb0fb936a8e43cc1e9f4/matplotlib-3.9.4-cp312-cp312-win_amd64.whl", hash = "sha256:a4a4cfc82330b27042a7169533da7991e8789d180dd5b3daeaee57d75cd5a03b", size = 7841160, upload-time = "2024-12-13T05:55:19.991Z" }, + { url = "https://files.pythonhosted.org/packages/49/b1/999f89a7556d101b23a2f0b54f1b6e140d73f56804da1398f2f0bc0924bc/matplotlib-3.9.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:37eeffeeca3c940985b80f5b9a7b95ea35671e0e7405001f249848d2b62351b6", size = 7891499, upload-time = "2024-12-13T05:55:22.142Z" }, + { url = "https://files.pythonhosted.org/packages/87/7b/06a32b13a684977653396a1bfcd34d4e7539c5d55c8cbfaa8ae04d47e4a9/matplotlib-3.9.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3e7465ac859ee4abcb0d836137cd8414e7bb7ad330d905abced457217d4f0f45", size = 7776802, upload-time = "2024-12-13T05:55:25.947Z" }, + { url = "https://files.pythonhosted.org/packages/65/87/ac498451aff739e515891bbb92e566f3c7ef31891aaa878402a71f9b0910/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f4c12302c34afa0cf061bea23b331e747e5e554b0fa595c96e01c7b75bc3b858", size = 8200802, upload-time = "2024-12-13T05:55:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/f8/6b/9eb761c00e1cb838f6c92e5f25dcda3f56a87a52f6cb8fdfa561e6cf6a13/matplotlib-3.9.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2b8c97917f21b75e72108b97707ba3d48f171541a74aa2a56df7a40626bafc64", size = 8313880, upload-time = "2024-12-13T05:55:30.965Z" }, + { url = "https://files.pythonhosted.org/packages/d7/a2/c8eaa600e2085eec7e38cbbcc58a30fc78f8224939d31d3152bdafc01fd1/matplotlib-3.9.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0229803bd7e19271b03cb09f27db76c918c467aa4ce2ae168171bc67c3f508df", size = 9094637, upload-time = "2024-12-13T05:55:33.701Z" }, + { url = "https://files.pythonhosted.org/packages/71/1f/c6e1daea55b7bfeb3d84c6cb1abc449f6a02b181e7e2a5e4db34c3afb793/matplotlib-3.9.4-cp313-cp313-win_amd64.whl", hash = "sha256:7c0d8ef442ebf56ff5e206f8083d08252ee738e04f3dc88ea882853a05488799", size = 7841311, upload-time = "2024-12-13T05:55:36.737Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3a/2757d3f7d388b14dd48f5a83bea65b6d69f000e86b8f28f74d86e0d375bd/matplotlib-3.9.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:a04c3b00066a688834356d196136349cb32f5e1003c55ac419e91585168b88fb", size = 7919989, upload-time = "2024-12-13T05:55:39.024Z" }, + { url = "https://files.pythonhosted.org/packages/24/28/f5077c79a4f521589a37fe1062d6a6ea3534e068213f7357e7cfffc2e17a/matplotlib-3.9.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:04c519587f6c210626741a1e9a68eefc05966ede24205db8982841826af5871a", size = 7809417, upload-time = "2024-12-13T05:55:42.412Z" }, + { url = "https://files.pythonhosted.org/packages/36/c8/c523fd2963156692916a8eb7d4069084cf729359f7955cf09075deddfeaf/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:308afbf1a228b8b525fcd5cec17f246bbbb63b175a3ef6eb7b4d33287ca0cf0c", size = 8226258, upload-time = "2024-12-13T05:55:47.259Z" }, + { url = "https://files.pythonhosted.org/packages/f6/88/499bf4b8fa9349b6f5c0cf4cead0ebe5da9d67769129f1b5651e5ac51fbc/matplotlib-3.9.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ddb3b02246ddcffd3ce98e88fed5b238bc5faff10dbbaa42090ea13241d15764", size = 8335849, upload-time = "2024-12-13T05:55:49.763Z" }, + { url = "https://files.pythonhosted.org/packages/b8/9f/20a4156b9726188646a030774ee337d5ff695a965be45ce4dbcb9312c170/matplotlib-3.9.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8a75287e9cb9eee48cb79ec1d806f75b29c0fde978cb7223a1f4c5848d696041", size = 9102152, upload-time = "2024-12-13T05:55:51.997Z" }, + { url = "https://files.pythonhosted.org/packages/10/11/237f9c3a4e8d810b1759b67ff2da7c32c04f9c80aa475e7beb36ed43a8fb/matplotlib-3.9.4-cp313-cp313t-win_amd64.whl", hash = "sha256:488deb7af140f0ba86da003e66e10d55ff915e152c78b4b66d231638400b1965", size = 7896987, upload-time = "2024-12-13T05:55:55.941Z" }, + { url = "https://files.pythonhosted.org/packages/56/eb/501b465c9fef28f158e414ea3a417913dc2ac748564c7ed41535f23445b4/matplotlib-3.9.4-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:3c3724d89a387ddf78ff88d2a30ca78ac2b4c89cf37f2db4bd453c34799e933c", size = 7885919, upload-time = "2024-12-13T05:55:59.66Z" }, + { url = "https://files.pythonhosted.org/packages/da/36/236fbd868b6c91309a5206bd90c3f881f4f44b2d997cd1d6239ef652f878/matplotlib-3.9.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d5f0a8430ffe23d7e32cfd86445864ccad141797f7d25b7c41759a5b5d17cfd7", size = 7771486, upload-time = "2024-12-13T05:56:04.264Z" }, + { url = "https://files.pythonhosted.org/packages/e0/4b/105caf2d54d5ed11d9f4335398f5103001a03515f2126c936a752ccf1461/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bb0141a21aef3b64b633dc4d16cbd5fc538b727e4958be82a0e1c92a234160e", size = 8201838, upload-time = "2024-12-13T05:56:06.792Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a7/bb01188fb4013d34d274caf44a2f8091255b0497438e8b6c0a7c1710c692/matplotlib-3.9.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:57aa235109e9eed52e2c2949db17da185383fa71083c00c6c143a60e07e0888c", size = 8314492, upload-time = "2024-12-13T05:56:09.964Z" }, + { url = "https://files.pythonhosted.org/packages/33/19/02e1a37f7141fc605b193e927d0a9cdf9dc124a20b9e68793f4ffea19695/matplotlib-3.9.4-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:b18c600061477ccfdd1e6fd050c33d8be82431700f3452b297a56d9ed7037abb", size = 9092500, upload-time = "2024-12-13T05:56:13.55Z" }, + { url = "https://files.pythonhosted.org/packages/57/68/c2feb4667adbf882ffa4b3e0ac9967f848980d9f8b5bebd86644aa67ce6a/matplotlib-3.9.4-cp39-cp39-win_amd64.whl", hash = "sha256:ef5f2d1b67d2d2145ff75e10f8c008bfbf71d45137c4b648c87193e7dd053eac", size = 7822962, upload-time = "2024-12-13T05:56:16.358Z" }, + { url = "https://files.pythonhosted.org/packages/0c/22/2ef6a364cd3f565442b0b055e0599744f1e4314ec7326cdaaa48a4d864d7/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:44e0ed786d769d85bc787b0606a53f2d8d2d1d3c8a2608237365e9121c1a338c", size = 7877995, upload-time = "2024-12-13T05:56:18.805Z" }, + { url = "https://files.pythonhosted.org/packages/87/b8/2737456e566e9f4d94ae76b8aa0d953d9acb847714f9a7ad80184474f5be/matplotlib-3.9.4-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:09debb9ce941eb23ecdbe7eab972b1c3e0276dcf01688073faff7b0f61d6c6ca", size = 7769300, upload-time = "2024-12-13T05:56:21.315Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1f/e709c6ec7b5321e6568769baa288c7178e60a93a9da9e682b39450da0e29/matplotlib-3.9.4-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcc53cf157a657bfd03afab14774d54ba73aa84d42cfe2480c91bd94873952db", size = 8313423, upload-time = "2024-12-13T05:56:26.719Z" }, + { url = "https://files.pythonhosted.org/packages/5e/b6/5a1f868782cd13f053a679984e222007ecff654a9bfbac6b27a65f4eeb05/matplotlib-3.9.4-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:ad45da51be7ad02387801fd154ef74d942f49fe3fcd26a64c94842ba7ec0d865", size = 7854624, upload-time = "2024-12-13T05:56:29.359Z" }, +] + +[[package]] +name = "matplotlib" +version = "3.10.8" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "contourpy", version = "1.3.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "contourpy", version = "1.3.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "cycler", marker = "python_full_version >= '3.10'" }, + { name = "fonttools", version = "4.61.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "kiwisolver", version = "1.4.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pillow", version = "12.1.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyparsing", marker = "python_full_version >= '3.10'" }, + { name = "python-dateutil", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/76/d3c6e3a13fe484ebe7718d14e269c9569c4eb0020a968a327acb3b9a8fe6/matplotlib-3.10.8.tar.gz", hash = "sha256:2299372c19d56bcd35cf05a2738308758d32b9eaed2371898d8f5bd33f084aa3", size = 34806269, upload-time = "2025-12-10T22:56:51.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/58/be/a30bd917018ad220c400169fba298f2bb7003c8ccbc0c3e24ae2aacad1e8/matplotlib-3.10.8-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:00270d217d6b20d14b584c521f810d60c5c78406dc289859776550df837dcda7", size = 8239828, upload-time = "2025-12-10T22:55:02.313Z" }, + { url = "https://files.pythonhosted.org/packages/58/27/ca01e043c4841078e82cf6e80a6993dfecd315c3d79f5f3153afbb8e1ec6/matplotlib-3.10.8-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:37b3c1cc42aa184b3f738cfa18c1c1d72fd496d85467a6cf7b807936d39aa656", size = 8128050, upload-time = "2025-12-10T22:55:04.997Z" }, + { url = "https://files.pythonhosted.org/packages/cb/aa/7ab67f2b729ae6a91bcf9dcac0affb95fb8c56f7fd2b2af894ae0b0cf6fa/matplotlib-3.10.8-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee40c27c795bda6a5292e9cff9890189d32f7e3a0bf04e0e3c9430c4a00c37df", size = 8700452, upload-time = "2025-12-10T22:55:07.47Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/2d5817b0acee3c49b7e7ccfbf5b273f284957cc8e270adf36375db353190/matplotlib-3.10.8-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a48f2b74020919552ea25d222d5cc6af9ca3f4eb43a93e14d068457f545c2a17", size = 9534928, upload-time = "2025-12-10T22:55:10.566Z" }, + { url = "https://files.pythonhosted.org/packages/c9/5b/8e66653e9f7c39cb2e5cab25fce4810daffa2bff02cbf5f3077cea9e942c/matplotlib-3.10.8-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f254d118d14a7f99d616271d6c3c27922c092dac11112670b157798b89bf4933", size = 9586377, upload-time = "2025-12-10T22:55:12.362Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e2/fd0bbadf837f81edb0d208ba8f8cb552874c3b16e27cb91a31977d90875d/matplotlib-3.10.8-cp310-cp310-win_amd64.whl", hash = "sha256:f9b587c9c7274c1613a30afabf65a272114cd6cdbe67b3406f818c79d7ab2e2a", size = 8128127, upload-time = "2025-12-10T22:55:14.436Z" }, + { url = "https://files.pythonhosted.org/packages/f8/86/de7e3a1cdcfc941483af70609edc06b83e7c8a0e0dc9ac325200a3f4d220/matplotlib-3.10.8-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:6be43b667360fef5c754dda5d25a32e6307a03c204f3c0fc5468b78fa87b4160", size = 8251215, upload-time = "2025-12-10T22:55:16.175Z" }, + { url = "https://files.pythonhosted.org/packages/fd/14/baad3222f424b19ce6ad243c71de1ad9ec6b2e4eb1e458a48fdc6d120401/matplotlib-3.10.8-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a2b336e2d91a3d7006864e0990c83b216fcdca64b5a6484912902cef87313d78", size = 8139625, upload-time = "2025-12-10T22:55:17.712Z" }, + { url = "https://files.pythonhosted.org/packages/8f/a0/7024215e95d456de5883e6732e708d8187d9753a21d32f8ddb3befc0c445/matplotlib-3.10.8-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:efb30e3baaea72ce5928e32bab719ab4770099079d66726a62b11b1ef7273be4", size = 8712614, upload-time = "2025-12-10T22:55:20.8Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f4/b8347351da9a5b3f41e26cf547252d861f685c6867d179a7c9d60ad50189/matplotlib-3.10.8-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d56a1efd5bfd61486c8bc968fa18734464556f0fb8e51690f4ac25d85cbbbbc2", size = 9540997, upload-time = "2025-12-10T22:55:23.258Z" }, + { url = "https://files.pythonhosted.org/packages/9e/c0/c7b914e297efe0bc36917bf216b2acb91044b91e930e878ae12981e461e5/matplotlib-3.10.8-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:238b7ce5717600615c895050239ec955d91f321c209dd110db988500558e70d6", size = 9596825, upload-time = "2025-12-10T22:55:25.217Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d3/a4bbc01c237ab710a1f22b4da72f4ff6d77eb4c7735ea9811a94ae239067/matplotlib-3.10.8-cp311-cp311-win_amd64.whl", hash = "sha256:18821ace09c763ec93aef5eeff087ee493a24051936d7b9ebcad9662f66501f9", size = 8135090, upload-time = "2025-12-10T22:55:27.162Z" }, + { url = "https://files.pythonhosted.org/packages/89/dd/a0b6588f102beab33ca6f5218b31725216577b2a24172f327eaf6417d5c9/matplotlib-3.10.8-cp311-cp311-win_arm64.whl", hash = "sha256:bab485bcf8b1c7d2060b4fcb6fc368a9e6f4cd754c9c2fea281f4be21df394a2", size = 8012377, upload-time = "2025-12-10T22:55:29.185Z" }, + { url = "https://files.pythonhosted.org/packages/9e/67/f997cdcbb514012eb0d10cd2b4b332667997fb5ebe26b8d41d04962fa0e6/matplotlib-3.10.8-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:64fcc24778ca0404ce0cb7b6b77ae1f4c7231cdd60e6778f999ee05cbd581b9a", size = 8260453, upload-time = "2025-12-10T22:55:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/7e/65/07d5f5c7f7c994f12c768708bd2e17a4f01a2b0f44a1c9eccad872433e2e/matplotlib-3.10.8-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b9a5ca4ac220a0cdd1ba6bcba3608547117d30468fefce49bb26f55c1a3d5c58", size = 8148321, upload-time = "2025-12-10T22:55:33.265Z" }, + { url = "https://files.pythonhosted.org/packages/3e/f3/c5195b1ae57ef85339fd7285dfb603b22c8b4e79114bae5f4f0fcf688677/matplotlib-3.10.8-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:3ab4aabc72de4ff77b3ec33a6d78a68227bf1123465887f9905ba79184a1cc04", size = 8716944, upload-time = "2025-12-10T22:55:34.922Z" }, + { url = "https://files.pythonhosted.org/packages/00/f9/7638f5cc82ec8a7aa005de48622eecc3ed7c9854b96ba15bd76b7fd27574/matplotlib-3.10.8-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:24d50994d8c5816ddc35411e50a86ab05f575e2530c02752e02538122613371f", size = 9550099, upload-time = "2025-12-10T22:55:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/57/61/78cd5920d35b29fd2a0fe894de8adf672ff52939d2e9b43cb83cd5ce1bc7/matplotlib-3.10.8-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:99eefd13c0dc3b3c1b4d561c1169e65fe47aab7b8158754d7c084088e2329466", size = 9613040, upload-time = "2025-12-10T22:55:38.715Z" }, + { url = "https://files.pythonhosted.org/packages/30/4e/c10f171b6e2f44d9e3a2b96efa38b1677439d79c99357600a62cc1e9594e/matplotlib-3.10.8-cp312-cp312-win_amd64.whl", hash = "sha256:dd80ecb295460a5d9d260df63c43f4afbdd832d725a531f008dad1664f458adf", size = 8142717, upload-time = "2025-12-10T22:55:41.103Z" }, + { url = "https://files.pythonhosted.org/packages/f1/76/934db220026b5fef85f45d51a738b91dea7d70207581063cd9bd8fafcf74/matplotlib-3.10.8-cp312-cp312-win_arm64.whl", hash = "sha256:3c624e43ed56313651bc18a47f838b60d7b8032ed348911c54906b130b20071b", size = 8012751, upload-time = "2025-12-10T22:55:42.684Z" }, + { url = "https://files.pythonhosted.org/packages/3d/b9/15fd5541ef4f5b9a17eefd379356cf12175fe577424e7b1d80676516031a/matplotlib-3.10.8-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3f2e409836d7f5ac2f1c013110a4d50b9f7edc26328c108915f9075d7d7a91b6", size = 8261076, upload-time = "2025-12-10T22:55:44.648Z" }, + { url = "https://files.pythonhosted.org/packages/8d/a0/2ba3473c1b66b9c74dc7107c67e9008cb1782edbe896d4c899d39ae9cf78/matplotlib-3.10.8-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:56271f3dac49a88d7fca5060f004d9d22b865f743a12a23b1e937a0be4818ee1", size = 8148794, upload-time = "2025-12-10T22:55:46.252Z" }, + { url = "https://files.pythonhosted.org/packages/75/97/a471f1c3eb1fd6f6c24a31a5858f443891d5127e63a7788678d14e249aea/matplotlib-3.10.8-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a0a7f52498f72f13d4a25ea70f35f4cb60642b466cbb0a9be951b5bc3f45a486", size = 8718474, upload-time = "2025-12-10T22:55:47.864Z" }, + { url = "https://files.pythonhosted.org/packages/01/be/cd478f4b66f48256f42927d0acbcd63a26a893136456cd079c0cc24fbabf/matplotlib-3.10.8-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:646d95230efb9ca614a7a594d4fcacde0ac61d25e37dd51710b36477594963ce", size = 9549637, upload-time = "2025-12-10T22:55:50.048Z" }, + { url = "https://files.pythonhosted.org/packages/5d/7c/8dc289776eae5109e268c4fb92baf870678dc048a25d4ac903683b86d5bf/matplotlib-3.10.8-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f89c151aab2e2e23cb3fe0acad1e8b82841fd265379c4cecd0f3fcb34c15e0f6", size = 9613678, upload-time = "2025-12-10T22:55:52.21Z" }, + { url = "https://files.pythonhosted.org/packages/64/40/37612487cc8a437d4dd261b32ca21fe2d79510fe74af74e1f42becb1bdb8/matplotlib-3.10.8-cp313-cp313-win_amd64.whl", hash = "sha256:e8ea3e2d4066083e264e75c829078f9e149fa119d27e19acd503de65e0b13149", size = 8142686, upload-time = "2025-12-10T22:55:54.253Z" }, + { url = "https://files.pythonhosted.org/packages/66/52/8d8a8730e968185514680c2a6625943f70269509c3dcfc0dcf7d75928cb8/matplotlib-3.10.8-cp313-cp313-win_arm64.whl", hash = "sha256:c108a1d6fa78a50646029cb6d49808ff0fc1330fda87fa6f6250c6b5369b6645", size = 8012917, upload-time = "2025-12-10T22:55:56.268Z" }, + { url = "https://files.pythonhosted.org/packages/b5/27/51fe26e1062f298af5ef66343d8ef460e090a27fea73036c76c35821df04/matplotlib-3.10.8-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:ad3d9833a64cf48cc4300f2b406c3d0f4f4724a91c0bd5640678a6ba7c102077", size = 8305679, upload-time = "2025-12-10T22:55:57.856Z" }, + { url = "https://files.pythonhosted.org/packages/2c/1e/4de865bc591ac8e3062e835f42dd7fe7a93168d519557837f0e37513f629/matplotlib-3.10.8-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:eb3823f11823deade26ce3b9f40dcb4a213da7a670013929f31d5f5ed1055b22", size = 8198336, upload-time = "2025-12-10T22:55:59.371Z" }, + { url = "https://files.pythonhosted.org/packages/c6/cb/2f7b6e75fb4dce87ef91f60cac4f6e34f4c145ab036a22318ec837971300/matplotlib-3.10.8-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d9050fee89a89ed57b4fb2c1bfac9a3d0c57a0d55aed95949eedbc42070fea39", size = 8731653, upload-time = "2025-12-10T22:56:01.032Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/bd9c57d6ba670a37ab31fb87ec3e8691b947134b201f881665b28cc039ff/matplotlib-3.10.8-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b44d07310e404ba95f8c25aa5536f154c0a8ec473303535949e52eb71d0a1565", size = 9561356, upload-time = "2025-12-10T22:56:02.95Z" }, + { url = "https://files.pythonhosted.org/packages/c0/3d/8b94a481456dfc9dfe6e39e93b5ab376e50998cddfd23f4ae3b431708f16/matplotlib-3.10.8-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0a33deb84c15ede243aead39f77e990469fff93ad1521163305095b77b72ce4a", size = 9614000, upload-time = "2025-12-10T22:56:05.411Z" }, + { url = "https://files.pythonhosted.org/packages/bd/cd/bc06149fe5585ba800b189a6a654a75f1f127e8aab02fd2be10df7fa500c/matplotlib-3.10.8-cp313-cp313t-win_amd64.whl", hash = "sha256:3a48a78d2786784cc2413e57397981fb45c79e968d99656706018d6e62e57958", size = 8220043, upload-time = "2025-12-10T22:56:07.551Z" }, + { url = "https://files.pythonhosted.org/packages/e3/de/b22cf255abec916562cc04eef457c13e58a1990048de0c0c3604d082355e/matplotlib-3.10.8-cp313-cp313t-win_arm64.whl", hash = "sha256:15d30132718972c2c074cd14638c7f4592bd98719e2308bccea40e0538bc0cb5", size = 8062075, upload-time = "2025-12-10T22:56:09.178Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/9c0ff7a2f11615e516c3b058e1e6e8f9614ddeca53faca06da267c48345d/matplotlib-3.10.8-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:b53285e65d4fa4c86399979e956235deb900be5baa7fc1218ea67fbfaeaadd6f", size = 8262481, upload-time = "2025-12-10T22:56:10.885Z" }, + { url = "https://files.pythonhosted.org/packages/6f/ca/e8ae28649fcdf039fda5ef554b40a95f50592a3c47e6f7270c9561c12b07/matplotlib-3.10.8-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:32f8dce744be5569bebe789e46727946041199030db8aeb2954d26013a0eb26b", size = 8151473, upload-time = "2025-12-10T22:56:12.377Z" }, + { url = "https://files.pythonhosted.org/packages/f1/6f/009d129ae70b75e88cbe7e503a12a4c0670e08ed748a902c2568909e9eb5/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4cf267add95b1c88300d96ca837833d4112756045364f5c734a2276038dae27d", size = 9553896, upload-time = "2025-12-10T22:56:14.432Z" }, + { url = "https://files.pythonhosted.org/packages/f5/26/4221a741eb97967bc1fd5e4c52b9aa5a91b2f4ec05b59f6def4d820f9df9/matplotlib-3.10.8-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2cf5bd12cecf46908f286d7838b2abc6c91cda506c0445b8223a7c19a00df008", size = 9824193, upload-time = "2025-12-10T22:56:16.29Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f3/3abf75f38605772cf48a9daf5821cd4f563472f38b4b828c6fba6fa6d06e/matplotlib-3.10.8-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:41703cc95688f2516b480f7f339d8851a6035f18e100ee6a32bc0b8536a12a9c", size = 9615444, upload-time = "2025-12-10T22:56:18.155Z" }, + { url = "https://files.pythonhosted.org/packages/93/a5/de89ac80f10b8dc615807ee1133cd99ac74082581196d4d9590bea10690d/matplotlib-3.10.8-cp314-cp314-win_amd64.whl", hash = "sha256:83d282364ea9f3e52363da262ce32a09dfe241e4080dcedda3c0db059d3c1f11", size = 8272719, upload-time = "2025-12-10T22:56:20.366Z" }, + { url = "https://files.pythonhosted.org/packages/69/ce/b006495c19ccc0a137b48083168a37bd056392dee02f87dba0472f2797fe/matplotlib-3.10.8-cp314-cp314-win_arm64.whl", hash = "sha256:2c1998e92cd5999e295a731bcb2911c75f597d937341f3030cc24ef2733d78a8", size = 8144205, upload-time = "2025-12-10T22:56:22.239Z" }, + { url = "https://files.pythonhosted.org/packages/68/d9/b31116a3a855bd313c6fcdb7226926d59b041f26061c6c5b1be66a08c826/matplotlib-3.10.8-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:b5a2b97dbdc7d4f353ebf343744f1d1f1cca8aa8bfddb4262fcf4306c3761d50", size = 8305785, upload-time = "2025-12-10T22:56:24.218Z" }, + { url = "https://files.pythonhosted.org/packages/1e/90/6effe8103f0272685767ba5f094f453784057072f49b393e3ea178fe70a5/matplotlib-3.10.8-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:3f5c3e4da343bba819f0234186b9004faba952cc420fbc522dc4e103c1985908", size = 8198361, upload-time = "2025-12-10T22:56:26.787Z" }, + { url = "https://files.pythonhosted.org/packages/d7/65/a73188711bea603615fc0baecca1061429ac16940e2385433cc778a9d8e7/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5f62550b9a30afde8c1c3ae450e5eb547d579dd69b25c2fc7a1c67f934c1717a", size = 9561357, upload-time = "2025-12-10T22:56:28.953Z" }, + { url = "https://files.pythonhosted.org/packages/f4/3d/b5c5d5d5be8ce63292567f0e2c43dde9953d3ed86ac2de0a72e93c8f07a1/matplotlib-3.10.8-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:495672de149445ec1b772ff2c9ede9b769e3cb4f0d0aa7fa730d7f59e2d4e1c1", size = 9823610, upload-time = "2025-12-10T22:56:31.455Z" }, + { url = "https://files.pythonhosted.org/packages/4d/4b/e7beb6bbd49f6bae727a12b270a2654d13c397576d25bd6786e47033300f/matplotlib-3.10.8-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:595ba4d8fe983b88f0eec8c26a241e16d6376fe1979086232f481f8f3f67494c", size = 9614011, upload-time = "2025-12-10T22:56:33.85Z" }, + { url = "https://files.pythonhosted.org/packages/7c/e6/76f2813d31f032e65f6f797e3f2f6e4aab95b65015924b1c51370395c28a/matplotlib-3.10.8-cp314-cp314t-win_amd64.whl", hash = "sha256:25d380fe8b1dc32cf8f0b1b448470a77afb195438bafdf1d858bfb876f3edf7b", size = 8362801, upload-time = "2025-12-10T22:56:36.107Z" }, + { url = "https://files.pythonhosted.org/packages/5d/49/d651878698a0b67f23aa28e17f45a6d6dd3d3f933fa29087fa4ce5947b5a/matplotlib-3.10.8-cp314-cp314t-win_arm64.whl", hash = "sha256:113bb52413ea508ce954a02c10ffd0d565f9c3bc7f2eddc27dfe1731e71c7b5f", size = 8192560, upload-time = "2025-12-10T22:56:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/f5/43/31d59500bb950b0d188e149a2e552040528c13d6e3d6e84d0cccac593dcd/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:f97aeb209c3d2511443f8797e3e5a569aebb040d4f8bc79aa3ee78a8fb9e3dd8", size = 8237252, upload-time = "2025-12-10T22:56:39.529Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2c/615c09984f3c5f907f51c886538ad785cf72e0e11a3225de2c0f9442aecc/matplotlib-3.10.8-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:fb061f596dad3a0f52b60dc6a5dec4a0c300dec41e058a7efe09256188d170b7", size = 8124693, upload-time = "2025-12-10T22:56:41.758Z" }, + { url = "https://files.pythonhosted.org/packages/91/e1/2757277a1c56041e1fc104b51a0f7b9a4afc8eb737865d63cababe30bc61/matplotlib-3.10.8-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:12d90df9183093fcd479f4172ac26b322b1248b15729cb57f42f71f24c7e37a3", size = 8702205, upload-time = "2025-12-10T22:56:43.415Z" }, + { url = "https://files.pythonhosted.org/packages/04/30/3afaa31c757f34b7725ab9d2ba8b48b5e89c2019c003e7d0ead143aabc5a/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:6da7c2ce169267d0d066adcf63758f0604aa6c3eebf67458930f9d9b79ad1db1", size = 8249198, upload-time = "2025-12-10T22:56:45.584Z" }, + { url = "https://files.pythonhosted.org/packages/48/2f/6334aec331f57485a642a7c8be03cb286f29111ae71c46c38b363230063c/matplotlib-3.10.8-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9153c3292705be9f9c64498a8872118540c3f4123d1a1c840172edf262c8be4a", size = 8136817, upload-time = "2025-12-10T22:56:47.339Z" }, + { url = "https://files.pythonhosted.org/packages/73/e4/6d6f14b2a759c622f191b2d67e9075a3f56aaccb3be4bb9bb6890030d0a0/matplotlib-3.10.8-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ae029229a57cd1e8fe542485f27e7ca7b23aa9e8944ddb4985d0bc444f1eca2", size = 8713867, upload-time = "2025-12-10T22:56:48.954Z" }, +] + +[[package]] +name = "matplotlib-inline" +version = "0.2.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "traitlets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c7/74/97e72a36efd4ae2bccb3463284300f8953f199b5ffbc04cbbb0ec78f74b1/matplotlib_inline-0.2.1.tar.gz", hash = "sha256:e1ee949c340d771fc39e241ea75683deb94762c8fa5f2927ec57c83c4dffa9fe", size = 8110, upload-time = "2025-10-23T09:00:22.126Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/33/ee4519fa02ed11a94aef9559552f3b17bb863f2ecfe1a35dc7f548cde231/matplotlib_inline-0.2.1-py3-none-any.whl", hash = "sha256:d56ce5156ba6085e00a9d54fead6ed29a9c47e215cd1bba2e976ef39f5710a76", size = 9516, upload-time = "2025-10-23T09:00:20.675Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "more-itertools" +version = "10.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ea/5d/38b681d3fce7a266dd9ab73c66959406d565b3e85f21d5e66e1181d93721/more_itertools-10.8.0.tar.gz", hash = "sha256:f638ddf8a1a0d134181275fb5d58b086ead7c6a72429ad725c67503f13ba30bd", size = 137431, upload-time = "2025-09-02T15:23:11.018Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a4/8e/469e5a4a2f5855992e425f3cb33804cc07bf18d48f2db061aec61ce50270/more_itertools-10.8.0-py3-none-any.whl", hash = "sha256:52d4362373dcf7c52546bc4af9a86ee7c4579df9a8dc268be0a2f949d376cc9b", size = 69667, upload-time = "2025-09-02T15:23:09.635Z" }, +] + +[[package]] +name = "multidict" +version = "6.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1a/c2/c2d94cbe6ac1753f3fc980da97b3d930efe1da3af3c9f5125354436c073d/multidict-6.7.1.tar.gz", hash = "sha256:ec6652a1bee61c53a3e5776b6049172c53b6aaba34f18c9ad04f82712bac623d", size = 102010, upload-time = "2026-01-26T02:46:45.979Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/0b/19348d4c98980c4851d2f943f8ebafdece2ae7ef737adcfa5994ce8e5f10/multidict-6.7.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c93c3db7ea657dd4637d57e74ab73de31bccefe144d3d4ce370052035bc85fb5", size = 77176, upload-time = "2026-01-26T02:42:59.784Z" }, + { url = "https://files.pythonhosted.org/packages/ef/04/9de3f8077852e3d438215c81e9b691244532d2e05b4270e89ce67b7d103c/multidict-6.7.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:974e72a2474600827abaeda71af0c53d9ebbc3c2eb7da37b37d7829ae31232d8", size = 44996, upload-time = "2026-01-26T02:43:01.674Z" }, + { url = "https://files.pythonhosted.org/packages/31/5c/08c7f7fe311f32e83f7621cd3f99d805f45519cd06fafb247628b861da7d/multidict-6.7.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdea2e7b2456cfb6694fb113066fd0ec7ea4d67e3a35e1f4cbeea0b448bf5872", size = 44631, upload-time = "2026-01-26T02:43:03.169Z" }, + { url = "https://files.pythonhosted.org/packages/b7/7f/0e3b1390ae772f27501199996b94b52ceeb64fe6f9120a32c6c3f6b781be/multidict-6.7.1-cp310-cp310-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17207077e29342fdc2c9a82e4b306f1127bf1ea91f8b71e02d4798a70bb99991", size = 242561, upload-time = "2026-01-26T02:43:04.733Z" }, + { url = "https://files.pythonhosted.org/packages/dd/f4/8719f4f167586af317b69dd3e90f913416c91ca610cac79a45c53f590312/multidict-6.7.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d4f49cb5661344764e4c7c7973e92a47a59b8fc19b6523649ec9dc4960e58a03", size = 242223, upload-time = "2026-01-26T02:43:06.695Z" }, + { url = "https://files.pythonhosted.org/packages/47/ab/7c36164cce64a6ad19c6d9a85377b7178ecf3b89f8fd589c73381a5eedfd/multidict-6.7.1-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:a9fc4caa29e2e6ae408d1c450ac8bf19892c5fca83ee634ecd88a53332c59981", size = 222322, upload-time = "2026-01-26T02:43:08.472Z" }, + { url = "https://files.pythonhosted.org/packages/f5/79/a25add6fb38035b5337bc5734f296d9afc99163403bbcf56d4170f97eb62/multidict-6.7.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c5f0c21549ab432b57dcc82130f388d84ad8179824cc3f223d5e7cfbfd4143f6", size = 254005, upload-time = "2026-01-26T02:43:10.127Z" }, + { url = "https://files.pythonhosted.org/packages/4a/7b/64a87cf98e12f756fc8bd444b001232ffff2be37288f018ad0d3f0aae931/multidict-6.7.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:7dfb78d966b2c906ae1d28ccf6e6712a3cd04407ee5088cd276fe8cb42186190", size = 251173, upload-time = "2026-01-26T02:43:11.731Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ac/b605473de2bb404e742f2cc3583d12aedb2352a70e49ae8fce455b50c5aa/multidict-6.7.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9b0d9b91d1aa44db9c1f1ecd0d9d2ae610b2f4f856448664e01a3b35899f3f92", size = 243273, upload-time = "2026-01-26T02:43:13.063Z" }, + { url = "https://files.pythonhosted.org/packages/03/65/11492d6a0e259783720f3bc1d9ea55579a76f1407e31ed44045c99542004/multidict-6.7.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:dd96c01a9dcd4889dcfcf9eb5544ca0c77603f239e3ffab0524ec17aea9a93ee", size = 238956, upload-time = "2026-01-26T02:43:14.843Z" }, + { url = "https://files.pythonhosted.org/packages/5f/a7/7ee591302af64e7c196fb63fe856c788993c1372df765102bd0448e7e165/multidict-6.7.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:067343c68cd6612d375710f895337b3a98a033c94f14b9a99eff902f205424e2", size = 233477, upload-time = "2026-01-26T02:43:16.025Z" }, + { url = "https://files.pythonhosted.org/packages/9c/99/c109962d58756c35fd9992fed7f2355303846ea2ff054bb5f5e9d6b888de/multidict-6.7.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:5884a04f4ff56c6120f6ccf703bdeb8b5079d808ba604d4d53aec0d55dc33568", size = 243615, upload-time = "2026-01-26T02:43:17.84Z" }, + { url = "https://files.pythonhosted.org/packages/d5/5f/1973e7c771c86e93dcfe1c9cc55a5481b610f6614acfc28c0d326fe6bfad/multidict-6.7.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8affcf1c98b82bc901702eb73b6947a1bfa170823c153fe8a47b5f5f02e48e40", size = 249930, upload-time = "2026-01-26T02:43:19.06Z" }, + { url = "https://files.pythonhosted.org/packages/5d/a5/f170fc2268c3243853580203378cd522446b2df632061e0a5409817854c7/multidict-6.7.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:0d17522c37d03e85c8098ec8431636309b2682cf12e58f4dbc76121fb50e4962", size = 243807, upload-time = "2026-01-26T02:43:20.286Z" }, + { url = "https://files.pythonhosted.org/packages/de/01/73856fab6d125e5bc652c3986b90e8699a95e84b48d72f39ade6c0e74a8c/multidict-6.7.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:24c0cf81544ca5e17cfcb6e482e7a82cd475925242b308b890c9452a074d4505", size = 239103, upload-time = "2026-01-26T02:43:21.508Z" }, + { url = "https://files.pythonhosted.org/packages/e7/46/f1220bd9944d8aa40d8ccff100eeeee19b505b857b6f603d6078cb5315b0/multidict-6.7.1-cp310-cp310-win32.whl", hash = "sha256:d82dd730a95e6643802f4454b8fdecdf08667881a9c5670db85bc5a56693f122", size = 41416, upload-time = "2026-01-26T02:43:22.703Z" }, + { url = "https://files.pythonhosted.org/packages/68/00/9b38e272a770303692fc406c36e1a4c740f401522d5787691eb38a8925a8/multidict-6.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:cf37cbe5ced48d417ba045aca1b21bafca67489452debcde94778a576666a1df", size = 46022, upload-time = "2026-01-26T02:43:23.77Z" }, + { url = "https://files.pythonhosted.org/packages/64/65/d8d42490c02ee07b6bbe00f7190d70bb4738b3cce7629aaf9f213ef730dd/multidict-6.7.1-cp310-cp310-win_arm64.whl", hash = "sha256:59bc83d3f66b41dac1e7460aac1d196edc70c9ba3094965c467715a70ecb46db", size = 43238, upload-time = "2026-01-26T02:43:24.882Z" }, + { url = "https://files.pythonhosted.org/packages/ce/f1/a90635c4f88fb913fbf4ce660b83b7445b7a02615bda034b2f8eb38fd597/multidict-6.7.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7ff981b266af91d7b4b3793ca3382e53229088d193a85dfad6f5f4c27fc73e5d", size = 76626, upload-time = "2026-01-26T02:43:26.485Z" }, + { url = "https://files.pythonhosted.org/packages/a6/9b/267e64eaf6fc637a15b35f5de31a566634a2740f97d8d094a69d34f524a4/multidict-6.7.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:844c5bca0b5444adb44a623fb0a1310c2f4cd41f402126bb269cd44c9b3f3e1e", size = 44706, upload-time = "2026-01-26T02:43:27.607Z" }, + { url = "https://files.pythonhosted.org/packages/dd/a4/d45caf2b97b035c57267791ecfaafbd59c68212004b3842830954bb4b02e/multidict-6.7.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f2a0a924d4c2e9afcd7ec64f9de35fcd96915149b2216e1cb2c10a56df483855", size = 44356, upload-time = "2026-01-26T02:43:28.661Z" }, + { url = "https://files.pythonhosted.org/packages/fd/d2/0a36c8473f0cbaeadd5db6c8b72d15bbceeec275807772bfcd059bef487d/multidict-6.7.1-cp311-cp311-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:8be1802715a8e892c784c0197c2ace276ea52702a0ede98b6310c8f255a5afb3", size = 244355, upload-time = "2026-01-26T02:43:31.165Z" }, + { url = "https://files.pythonhosted.org/packages/5d/16/8c65be997fd7dd311b7d39c7b6e71a0cb449bad093761481eccbbe4b42a2/multidict-6.7.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2e2d2ed645ea29f31c4c7ea1552fcfd7cb7ba656e1eafd4134a6620c9f5fdd9e", size = 246433, upload-time = "2026-01-26T02:43:32.581Z" }, + { url = "https://files.pythonhosted.org/packages/01/fb/4dbd7e848d2799c6a026ec88ad39cf2b8416aa167fcc903baa55ecaa045c/multidict-6.7.1-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:95922cee9a778659e91db6497596435777bd25ed116701a4c034f8e46544955a", size = 225376, upload-time = "2026-01-26T02:43:34.417Z" }, + { url = "https://files.pythonhosted.org/packages/b6/8a/4a3a6341eac3830f6053062f8fbc9a9e54407c80755b3f05bc427295c2d0/multidict-6.7.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6b83cabdc375ffaaa15edd97eb7c0c672ad788e2687004990074d7d6c9b140c8", size = 257365, upload-time = "2026-01-26T02:43:35.741Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a2/dd575a69c1aa206e12d27d0770cdf9b92434b48a9ef0cd0d1afdecaa93c4/multidict-6.7.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:38fb49540705369bab8484db0689d86c0a33a0a9f2c1b197f506b71b4b6c19b0", size = 254747, upload-time = "2026-01-26T02:43:36.976Z" }, + { url = "https://files.pythonhosted.org/packages/5a/56/21b27c560c13822ed93133f08aa6372c53a8e067f11fbed37b4adcdac922/multidict-6.7.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:439cbebd499f92e9aa6793016a8acaa161dfa749ae86d20960189f5398a19144", size = 246293, upload-time = "2026-01-26T02:43:38.258Z" }, + { url = "https://files.pythonhosted.org/packages/5a/a4/23466059dc3854763423d0ad6c0f3683a379d97673b1b89ec33826e46728/multidict-6.7.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6d3bc717b6fe763b8be3f2bee2701d3c8eb1b2a8ae9f60910f1b2860c82b6c49", size = 242962, upload-time = "2026-01-26T02:43:40.034Z" }, + { url = "https://files.pythonhosted.org/packages/1f/67/51dd754a3524d685958001e8fa20a0f5f90a6a856e0a9dcabff69be3dbb7/multidict-6.7.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:619e5a1ac57986dbfec9f0b301d865dddf763696435e2962f6d9cf2fdff2bb71", size = 237360, upload-time = "2026-01-26T02:43:41.752Z" }, + { url = "https://files.pythonhosted.org/packages/64/3f/036dfc8c174934d4b55d86ff4f978e558b0e585cef70cfc1ad01adc6bf18/multidict-6.7.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0b38ebffd9be37c1170d33bc0f36f4f262e0a09bc1aac1c34c7aa51a7293f0b3", size = 245940, upload-time = "2026-01-26T02:43:43.042Z" }, + { url = "https://files.pythonhosted.org/packages/3d/20/6214d3c105928ebc353a1c644a6ef1408bc5794fcb4f170bb524a3c16311/multidict-6.7.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:10ae39c9cfe6adedcdb764f5e8411d4a92b055e35573a2eaa88d3323289ef93c", size = 253502, upload-time = "2026-01-26T02:43:44.371Z" }, + { url = "https://files.pythonhosted.org/packages/b1/e2/c653bc4ae1be70a0f836b82172d643fcf1dade042ba2676ab08ec08bff0f/multidict-6.7.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:25167cc263257660290fba06b9318d2026e3c910be240a146e1f66dd114af2b0", size = 247065, upload-time = "2026-01-26T02:43:45.745Z" }, + { url = "https://files.pythonhosted.org/packages/c8/11/a854b4154cd3bd8b1fd375e8a8ca9d73be37610c361543d56f764109509b/multidict-6.7.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:128441d052254f42989ef98b7b6a6ecb1e6f708aa962c7984235316db59f50fa", size = 241870, upload-time = "2026-01-26T02:43:47.054Z" }, + { url = "https://files.pythonhosted.org/packages/13/bf/9676c0392309b5fdae322333d22a829715b570edb9baa8016a517b55b558/multidict-6.7.1-cp311-cp311-win32.whl", hash = "sha256:d62b7f64ffde3b99d06b707a280db04fb3855b55f5a06df387236051d0668f4a", size = 41302, upload-time = "2026-01-26T02:43:48.753Z" }, + { url = "https://files.pythonhosted.org/packages/c9/68/f16a3a8ba6f7b6dc92a1f19669c0810bd2c43fc5a02da13b1cbf8e253845/multidict-6.7.1-cp311-cp311-win_amd64.whl", hash = "sha256:bdbf9f3b332abd0cdb306e7c2113818ab1e922dc84b8f8fd06ec89ed2a19ab8b", size = 45981, upload-time = "2026-01-26T02:43:49.921Z" }, + { url = "https://files.pythonhosted.org/packages/ac/ad/9dd5305253fa00cd3c7555dbef69d5bf4133debc53b87ab8d6a44d411665/multidict-6.7.1-cp311-cp311-win_arm64.whl", hash = "sha256:b8c990b037d2fff2f4e33d3f21b9b531c5745b33a49a7d6dbe7a177266af44f6", size = 43159, upload-time = "2026-01-26T02:43:51.635Z" }, + { url = "https://files.pythonhosted.org/packages/8d/9c/f20e0e2cf80e4b2e4b1c365bf5fe104ee633c751a724246262db8f1a0b13/multidict-6.7.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:a90f75c956e32891a4eda3639ce6dd86e87105271f43d43442a3aedf3cddf172", size = 76893, upload-time = "2026-01-26T02:43:52.754Z" }, + { url = "https://files.pythonhosted.org/packages/fe/cf/18ef143a81610136d3da8193da9d80bfe1cb548a1e2d1c775f26b23d024a/multidict-6.7.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:3fccb473e87eaa1382689053e4a4618e7ba7b9b9b8d6adf2027ee474597128cd", size = 45456, upload-time = "2026-01-26T02:43:53.893Z" }, + { url = "https://files.pythonhosted.org/packages/a9/65/1caac9d4cd32e8433908683446eebc953e82d22b03d10d41a5f0fefe991b/multidict-6.7.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:b0fa96985700739c4c7853a43c0b3e169360d6855780021bfc6d0f1ce7c123e7", size = 43872, upload-time = "2026-01-26T02:43:55.041Z" }, + { url = "https://files.pythonhosted.org/packages/cf/3b/d6bd75dc4f3ff7c73766e04e705b00ed6dbbaccf670d9e05a12b006f5a21/multidict-6.7.1-cp312-cp312-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:cb2a55f408c3043e42b40cc8eecd575afa27b7e0b956dfb190de0f8499a57a53", size = 251018, upload-time = "2026-01-26T02:43:56.198Z" }, + { url = "https://files.pythonhosted.org/packages/fd/80/c959c5933adedb9ac15152e4067c702a808ea183a8b64cf8f31af8ad3155/multidict-6.7.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:eb0ce7b2a32d09892b3dd6cc44877a0d02a33241fafca5f25c8b6b62374f8b75", size = 258883, upload-time = "2026-01-26T02:43:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/86/85/7ed40adafea3d4f1c8b916e3b5cc3a8e07dfcdcb9cd72800f4ed3ca1b387/multidict-6.7.1-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:c3a32d23520ee37bf327d1e1a656fec76a2edd5c038bf43eddfa0572ec49c60b", size = 242413, upload-time = "2026-01-26T02:43:58.755Z" }, + { url = "https://files.pythonhosted.org/packages/d2/57/b8565ff533e48595503c785f8361ff9a4fde4d67de25c207cd0ba3befd03/multidict-6.7.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9c90fed18bffc0189ba814749fdcc102b536e83a9f738a9003e569acd540a733", size = 268404, upload-time = "2026-01-26T02:44:00.216Z" }, + { url = "https://files.pythonhosted.org/packages/e0/50/9810c5c29350f7258180dfdcb2e52783a0632862eb334c4896ac717cebcb/multidict-6.7.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:da62917e6076f512daccfbbde27f46fed1c98fee202f0559adec8ee0de67f71a", size = 269456, upload-time = "2026-01-26T02:44:02.202Z" }, + { url = "https://files.pythonhosted.org/packages/f3/8d/5e5be3ced1d12966fefb5c4ea3b2a5b480afcea36406559442c6e31d4a48/multidict-6.7.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfde23ef6ed9db7eaee6c37dcec08524cb43903c60b285b172b6c094711b3961", size = 256322, upload-time = "2026-01-26T02:44:03.56Z" }, + { url = "https://files.pythonhosted.org/packages/31/6e/d8a26d81ac166a5592782d208dd90dfdc0a7a218adaa52b45a672b46c122/multidict-6.7.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3758692429e4e32f1ba0df23219cd0b4fc0a52f476726fff9337d1a57676a582", size = 253955, upload-time = "2026-01-26T02:44:04.845Z" }, + { url = "https://files.pythonhosted.org/packages/59/4c/7c672c8aad41534ba619bcd4ade7a0dc87ed6b8b5c06149b85d3dd03f0cd/multidict-6.7.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:398c1478926eca669f2fd6a5856b6de9c0acf23a2cb59a14c0ba5844fa38077e", size = 251254, upload-time = "2026-01-26T02:44:06.133Z" }, + { url = "https://files.pythonhosted.org/packages/7b/bd/84c24de512cbafbdbc39439f74e967f19570ce7924e3007174a29c348916/multidict-6.7.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:c102791b1c4f3ab36ce4101154549105a53dc828f016356b3e3bcae2e3a039d3", size = 252059, upload-time = "2026-01-26T02:44:07.518Z" }, + { url = "https://files.pythonhosted.org/packages/fa/ba/f5449385510825b73d01c2d4087bf6d2fccc20a2d42ac34df93191d3dd03/multidict-6.7.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:a088b62bd733e2ad12c50dad01b7d0166c30287c166e137433d3b410add807a6", size = 263588, upload-time = "2026-01-26T02:44:09.382Z" }, + { url = "https://files.pythonhosted.org/packages/d7/11/afc7c677f68f75c84a69fe37184f0f82fce13ce4b92f49f3db280b7e92b3/multidict-6.7.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:3d51ff4785d58d3f6c91bdbffcb5e1f7ddfda557727043aa20d20ec4f65e324a", size = 259642, upload-time = "2026-01-26T02:44:10.73Z" }, + { url = "https://files.pythonhosted.org/packages/2b/17/ebb9644da78c4ab36403739e0e6e0e30ebb135b9caf3440825001a0bddcb/multidict-6.7.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fc5907494fccf3e7d3f94f95c91d6336b092b5fc83811720fae5e2765890dfba", size = 251377, upload-time = "2026-01-26T02:44:12.042Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a4/840f5b97339e27846c46307f2530a2805d9d537d8b8bd416af031cad7fa0/multidict-6.7.1-cp312-cp312-win32.whl", hash = "sha256:28ca5ce2fd9716631133d0e9a9b9a745ad7f60bac2bccafb56aa380fc0b6c511", size = 41887, upload-time = "2026-01-26T02:44:14.245Z" }, + { url = "https://files.pythonhosted.org/packages/80/31/0b2517913687895f5904325c2069d6a3b78f66cc641a86a2baf75a05dcbb/multidict-6.7.1-cp312-cp312-win_amd64.whl", hash = "sha256:fcee94dfbd638784645b066074b338bc9cc155d4b4bffa4adce1615c5a426c19", size = 46053, upload-time = "2026-01-26T02:44:15.371Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/aba28e4ee4006ae4c7df8d327d31025d760ffa992ea23812a601d226e682/multidict-6.7.1-cp312-cp312-win_arm64.whl", hash = "sha256:ba0a9fb644d0c1a2194cf7ffb043bd852cea63a57f66fbd33959f7dae18517bf", size = 43307, upload-time = "2026-01-26T02:44:16.852Z" }, + { url = "https://files.pythonhosted.org/packages/f2/22/929c141d6c0dba87d3e1d38fbdf1ba8baba86b7776469f2bc2d3227a1e67/multidict-6.7.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:2b41f5fed0ed563624f1c17630cb9941cf2309d4df00e494b551b5f3e3d67a23", size = 76174, upload-time = "2026-01-26T02:44:18.509Z" }, + { url = "https://files.pythonhosted.org/packages/c7/75/bc704ae15fee974f8fccd871305e254754167dce5f9e42d88a2def741a1d/multidict-6.7.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84e61e3af5463c19b67ced91f6c634effb89ef8bfc5ca0267f954451ed4bb6a2", size = 45116, upload-time = "2026-01-26T02:44:19.745Z" }, + { url = "https://files.pythonhosted.org/packages/79/76/55cd7186f498ed080a18440c9013011eb548f77ae1b297206d030eb1180a/multidict-6.7.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:935434b9853c7c112eee7ac891bc4cb86455aa631269ae35442cb316790c1445", size = 43524, upload-time = "2026-01-26T02:44:21.571Z" }, + { url = "https://files.pythonhosted.org/packages/e9/3c/414842ef8d5a1628d68edee29ba0e5bcf235dbfb3ccd3ea303a7fe8c72ff/multidict-6.7.1-cp313-cp313-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:432feb25a1cb67fe82a9680b4d65fb542e4635cb3166cd9c01560651ad60f177", size = 249368, upload-time = "2026-01-26T02:44:22.803Z" }, + { url = "https://files.pythonhosted.org/packages/f6/32/befed7f74c458b4a525e60519fe8d87eef72bb1e99924fa2b0f9d97a221e/multidict-6.7.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e82d14e3c948952a1a85503817e038cba5905a3352de76b9a465075d072fba23", size = 256952, upload-time = "2026-01-26T02:44:24.306Z" }, + { url = "https://files.pythonhosted.org/packages/03/d6/c878a44ba877f366630c860fdf74bfb203c33778f12b6ac274936853c451/multidict-6.7.1-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:4cfb48c6ea66c83bcaaf7e4dfa7ec1b6bbcf751b7db85a328902796dfde4c060", size = 240317, upload-time = "2026-01-26T02:44:25.772Z" }, + { url = "https://files.pythonhosted.org/packages/68/49/57421b4d7ad2e9e60e25922b08ceb37e077b90444bde6ead629095327a6f/multidict-6.7.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:1d540e51b7e8e170174555edecddbd5538105443754539193e3e1061864d444d", size = 267132, upload-time = "2026-01-26T02:44:27.648Z" }, + { url = "https://files.pythonhosted.org/packages/b7/fe/ec0edd52ddbcea2a2e89e174f0206444a61440b40f39704e64dc807a70bd/multidict-6.7.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:273d23f4b40f3dce4d6c8a821c741a86dec62cded82e1175ba3d99be128147ed", size = 268140, upload-time = "2026-01-26T02:44:29.588Z" }, + { url = "https://files.pythonhosted.org/packages/b0/73/6e1b01cbeb458807aa0831742232dbdd1fa92bfa33f52a3f176b4ff3dc11/multidict-6.7.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d624335fd4fa1c08a53f8b4be7676ebde19cd092b3895c421045ca87895b429", size = 254277, upload-time = "2026-01-26T02:44:30.902Z" }, + { url = "https://files.pythonhosted.org/packages/6a/b2/5fb8c124d7561a4974c342bc8c778b471ebbeb3cc17df696f034a7e9afe7/multidict-6.7.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:12fad252f8b267cc75b66e8fc51b3079604e8d43a75428ffe193cd9e2195dfd6", size = 252291, upload-time = "2026-01-26T02:44:32.31Z" }, + { url = "https://files.pythonhosted.org/packages/5a/96/51d4e4e06bcce92577fcd488e22600bd38e4fd59c20cb49434d054903bd2/multidict-6.7.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:03ede2a6ffbe8ef936b92cb4529f27f42be7f56afcdab5ab739cd5f27fb1cbf9", size = 250156, upload-time = "2026-01-26T02:44:33.734Z" }, + { url = "https://files.pythonhosted.org/packages/db/6b/420e173eec5fba721a50e2a9f89eda89d9c98fded1124f8d5c675f7a0c0f/multidict-6.7.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:90efbcf47dbe33dcf643a1e400d67d59abeac5db07dc3f27d6bdeae497a2198c", size = 249742, upload-time = "2026-01-26T02:44:35.222Z" }, + { url = "https://files.pythonhosted.org/packages/44/a3/ec5b5bd98f306bc2aa297b8c6f11a46714a56b1e6ef5ebda50a4f5d7c5fb/multidict-6.7.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:5c4b9bfc148f5a91be9244d6264c53035c8a0dcd2f51f1c3c6e30e30ebaa1c84", size = 262221, upload-time = "2026-01-26T02:44:36.604Z" }, + { url = "https://files.pythonhosted.org/packages/cd/f7/e8c0d0da0cd1e28d10e624604e1a36bcc3353aaebdfdc3a43c72bc683a12/multidict-6.7.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:401c5a650f3add2472d1d288c26deebc540f99e2fb83e9525007a74cd2116f1d", size = 258664, upload-time = "2026-01-26T02:44:38.008Z" }, + { url = "https://files.pythonhosted.org/packages/52/da/151a44e8016dd33feed44f730bd856a66257c1ee7aed4f44b649fb7edeb3/multidict-6.7.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:97891f3b1b3ffbded884e2916cacf3c6fc87b66bb0dde46f7357404750559f33", size = 249490, upload-time = "2026-01-26T02:44:39.386Z" }, + { url = "https://files.pythonhosted.org/packages/87/af/a3b86bf9630b732897f6fc3f4c4714b90aa4361983ccbdcd6c0339b21b0c/multidict-6.7.1-cp313-cp313-win32.whl", hash = "sha256:e1c5988359516095535c4301af38d8a8838534158f649c05dd1050222321bcb3", size = 41695, upload-time = "2026-01-26T02:44:41.318Z" }, + { url = "https://files.pythonhosted.org/packages/b2/35/e994121b0e90e46134673422dd564623f93304614f5d11886b1b3e06f503/multidict-6.7.1-cp313-cp313-win_amd64.whl", hash = "sha256:960c83bf01a95b12b08fd54324a4eb1d5b52c88932b5cba5d6e712bb3ed12eb5", size = 45884, upload-time = "2026-01-26T02:44:42.488Z" }, + { url = "https://files.pythonhosted.org/packages/ca/61/42d3e5dbf661242a69c97ea363f2d7b46c567da8eadef8890022be6e2ab0/multidict-6.7.1-cp313-cp313-win_arm64.whl", hash = "sha256:563fe25c678aaba333d5399408f5ec3c383ca5b663e7f774dd179a520b8144df", size = 43122, upload-time = "2026-01-26T02:44:43.664Z" }, + { url = "https://files.pythonhosted.org/packages/6d/b3/e6b21c6c4f314bb956016b0b3ef2162590a529b84cb831c257519e7fde44/multidict-6.7.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:c76c4bec1538375dad9d452d246ca5368ad6e1c9039dadcf007ae59c70619ea1", size = 83175, upload-time = "2026-01-26T02:44:44.894Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/23ecd2abfe0957b234f6c960f4ade497f55f2c16aeb684d4ecdbf1c95791/multidict-6.7.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:57b46b24b5d5ebcc978da4ec23a819a9402b4228b8a90d9c656422b4bdd8a963", size = 48460, upload-time = "2026-01-26T02:44:46.106Z" }, + { url = "https://files.pythonhosted.org/packages/c4/57/a0ed92b23f3a042c36bc4227b72b97eca803f5f1801c1ab77c8a212d455e/multidict-6.7.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e954b24433c768ce78ab7929e84ccf3422e46deb45a4dc9f93438f8217fa2d34", size = 46930, upload-time = "2026-01-26T02:44:47.278Z" }, + { url = "https://files.pythonhosted.org/packages/b5/66/02ec7ace29162e447f6382c495dc95826bf931d3818799bbef11e8f7df1a/multidict-6.7.1-cp313-cp313t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:3bd231490fa7217cc832528e1cd8752a96f0125ddd2b5749390f7c3ec8721b65", size = 242582, upload-time = "2026-01-26T02:44:48.604Z" }, + { url = "https://files.pythonhosted.org/packages/58/18/64f5a795e7677670e872673aca234162514696274597b3708b2c0d276cce/multidict-6.7.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:253282d70d67885a15c8a7716f3a73edf2d635793ceda8173b9ecc21f2fb8292", size = 250031, upload-time = "2026-01-26T02:44:50.544Z" }, + { url = "https://files.pythonhosted.org/packages/c8/ed/e192291dbbe51a8290c5686f482084d31bcd9d09af24f63358c3d42fd284/multidict-6.7.1-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:0b4c48648d7649c9335cf1927a8b87fa692de3dcb15faa676c6a6f1f1aabda43", size = 228596, upload-time = "2026-01-26T02:44:51.951Z" }, + { url = "https://files.pythonhosted.org/packages/1e/7e/3562a15a60cf747397e7f2180b0a11dc0c38d9175a650e75fa1b4d325e15/multidict-6.7.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:98bc624954ec4d2c7cb074b8eefc2b5d0ce7d482e410df446414355d158fe4ca", size = 257492, upload-time = "2026-01-26T02:44:53.902Z" }, + { url = "https://files.pythonhosted.org/packages/24/02/7d0f9eae92b5249bb50ac1595b295f10e263dd0078ebb55115c31e0eaccd/multidict-6.7.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:1b99af4d9eec0b49927b4402bcbb58dea89d3e0db8806a4086117019939ad3dd", size = 255899, upload-time = "2026-01-26T02:44:55.316Z" }, + { url = "https://files.pythonhosted.org/packages/00/e3/9b60ed9e23e64c73a5cde95269ef1330678e9c6e34dd4eb6b431b85b5a10/multidict-6.7.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6aac4f16b472d5b7dc6f66a0d49dd57b0e0902090be16594dc9ebfd3d17c47e7", size = 247970, upload-time = "2026-01-26T02:44:56.783Z" }, + { url = "https://files.pythonhosted.org/packages/3e/06/538e58a63ed5cfb0bd4517e346b91da32fde409d839720f664e9a4ae4f9d/multidict-6.7.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:21f830fe223215dffd51f538e78c172ed7c7f60c9b96a2bf05c4848ad49921c3", size = 245060, upload-time = "2026-01-26T02:44:58.195Z" }, + { url = "https://files.pythonhosted.org/packages/b2/2f/d743a3045a97c895d401e9bd29aaa09b94f5cbdf1bd561609e5a6c431c70/multidict-6.7.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f5dd81c45b05518b9aa4da4aa74e1c93d715efa234fd3e8a179df611cc85e5f4", size = 235888, upload-time = "2026-01-26T02:44:59.57Z" }, + { url = "https://files.pythonhosted.org/packages/38/83/5a325cac191ab28b63c52f14f1131f3b0a55ba3b9aa65a6d0bf2a9b921a0/multidict-6.7.1-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:eb304767bca2bb92fb9c5bd33cedc95baee5bb5f6c88e63706533a1c06ad08c8", size = 243554, upload-time = "2026-01-26T02:45:01.054Z" }, + { url = "https://files.pythonhosted.org/packages/20/1f/9d2327086bd15da2725ef6aae624208e2ef828ed99892b17f60c344e57ed/multidict-6.7.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:c9035dde0f916702850ef66460bc4239d89d08df4d02023a5926e7446724212c", size = 252341, upload-time = "2026-01-26T02:45:02.484Z" }, + { url = "https://files.pythonhosted.org/packages/e8/2c/2a1aa0280cf579d0f6eed8ee5211c4f1730bd7e06c636ba2ee6aafda302e/multidict-6.7.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:af959b9beeb66c822380f222f0e0a1889331597e81f1ded7f374f3ecb0fd6c52", size = 246391, upload-time = "2026-01-26T02:45:03.862Z" }, + { url = "https://files.pythonhosted.org/packages/e5/03/7ca022ffc36c5a3f6e03b179a5ceb829be9da5783e6fe395f347c0794680/multidict-6.7.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:41f2952231456154ee479651491e94118229844dd7226541788be783be2b5108", size = 243422, upload-time = "2026-01-26T02:45:05.296Z" }, + { url = "https://files.pythonhosted.org/packages/dc/1d/b31650eab6c5778aceed46ba735bd97f7c7d2f54b319fa916c0f96e7805b/multidict-6.7.1-cp313-cp313t-win32.whl", hash = "sha256:df9f19c28adcb40b6aae30bbaa1478c389efd50c28d541d76760199fc1037c32", size = 47770, upload-time = "2026-01-26T02:45:06.754Z" }, + { url = "https://files.pythonhosted.org/packages/ac/5b/2d2d1d522e51285bd61b1e20df8f47ae1a9d80839db0b24ea783b3832832/multidict-6.7.1-cp313-cp313t-win_amd64.whl", hash = "sha256:d54ecf9f301853f2c5e802da559604b3e95bb7a3b01a9c295c6ee591b9882de8", size = 53109, upload-time = "2026-01-26T02:45:08.044Z" }, + { url = "https://files.pythonhosted.org/packages/3d/a3/cc409ba012c83ca024a308516703cf339bdc4b696195644a7215a5164a24/multidict-6.7.1-cp313-cp313t-win_arm64.whl", hash = "sha256:5a37ca18e360377cfda1d62f5f382ff41f2b8c4ccb329ed974cc2e1643440118", size = 45573, upload-time = "2026-01-26T02:45:09.349Z" }, + { url = "https://files.pythonhosted.org/packages/91/cc/db74228a8be41884a567e88a62fd589a913708fcf180d029898c17a9a371/multidict-6.7.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8f333ec9c5eb1b7105e3b84b53141e66ca05a19a605368c55450b6ba208cb9ee", size = 75190, upload-time = "2026-01-26T02:45:10.651Z" }, + { url = "https://files.pythonhosted.org/packages/d5/22/492f2246bb5b534abd44804292e81eeaf835388901f0c574bac4eeec73c5/multidict-6.7.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a407f13c188f804c759fc6a9f88286a565c242a76b27626594c133b82883b5c2", size = 44486, upload-time = "2026-01-26T02:45:11.938Z" }, + { url = "https://files.pythonhosted.org/packages/f1/4f/733c48f270565d78b4544f2baddc2fb2a245e5a8640254b12c36ac7ac68e/multidict-6.7.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0e161ddf326db5577c3a4cc2d8648f81456e8a20d40415541587a71620d7a7d1", size = 43219, upload-time = "2026-01-26T02:45:14.346Z" }, + { url = "https://files.pythonhosted.org/packages/24/bb/2c0c2287963f4259c85e8bcbba9182ced8d7fca65c780c38e99e61629d11/multidict-6.7.1-cp314-cp314-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:1e3a8bb24342a8201d178c3b4984c26ba81a577c80d4d525727427460a50c22d", size = 245132, upload-time = "2026-01-26T02:45:15.712Z" }, + { url = "https://files.pythonhosted.org/packages/a7/f9/44d4b3064c65079d2467888794dea218d1601898ac50222ab8a9a8094460/multidict-6.7.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97231140a50f5d447d3164f994b86a0bed7cd016e2682f8650d6a9158e14fd31", size = 252420, upload-time = "2026-01-26T02:45:17.293Z" }, + { url = "https://files.pythonhosted.org/packages/8b/13/78f7275e73fa17b24c9a51b0bd9d73ba64bb32d0ed51b02a746eb876abe7/multidict-6.7.1-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:6b10359683bd8806a200fd2909e7c8ca3a7b24ec1d8132e483d58e791d881048", size = 233510, upload-time = "2026-01-26T02:45:19.356Z" }, + { url = "https://files.pythonhosted.org/packages/4b/25/8167187f62ae3cbd52da7893f58cb036b47ea3fb67138787c76800158982/multidict-6.7.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:283ddac99f7ac25a4acadbf004cb5ae34480bbeb063520f70ce397b281859362", size = 264094, upload-time = "2026-01-26T02:45:20.834Z" }, + { url = "https://files.pythonhosted.org/packages/a1/e7/69a3a83b7b030cf283fb06ce074a05a02322359783424d7edf0f15fe5022/multidict-6.7.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:538cec1e18c067d0e6103aa9a74f9e832904c957adc260e61cd9d8cf0c3b3d37", size = 260786, upload-time = "2026-01-26T02:45:22.818Z" }, + { url = "https://files.pythonhosted.org/packages/fe/3b/8ec5074bcfc450fe84273713b4b0a0dd47c0249358f5d82eb8104ffe2520/multidict-6.7.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7eee46ccb30ff48a1e35bb818cc90846c6be2b68240e42a78599166722cea709", size = 248483, upload-time = "2026-01-26T02:45:24.368Z" }, + { url = "https://files.pythonhosted.org/packages/48/5a/d5a99e3acbca0e29c5d9cba8f92ceb15dce78bab963b308ae692981e3a5d/multidict-6.7.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa263a02f4f2dd2d11a7b1bb4362aa7cb1049f84a9235d31adf63f30143469a0", size = 248403, upload-time = "2026-01-26T02:45:25.982Z" }, + { url = "https://files.pythonhosted.org/packages/35/48/e58cd31f6c7d5102f2a4bf89f96b9cf7e00b6c6f3d04ecc44417c00a5a3c/multidict-6.7.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:2e1425e2f99ec5bd36c15a01b690a1a2456209c5deed58f95469ffb46039ccbb", size = 240315, upload-time = "2026-01-26T02:45:27.487Z" }, + { url = "https://files.pythonhosted.org/packages/94/33/1cd210229559cb90b6786c30676bb0c58249ff42f942765f88793b41fdce/multidict-6.7.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:497394b3239fc6f0e13a78a3e1b61296e72bf1c5f94b4c4eb80b265c37a131cd", size = 245528, upload-time = "2026-01-26T02:45:28.991Z" }, + { url = "https://files.pythonhosted.org/packages/64/f2/6e1107d226278c876c783056b7db43d800bb64c6131cec9c8dfb6903698e/multidict-6.7.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:233b398c29d3f1b9676b4b6f75c518a06fcb2ea0b925119fb2c1bc35c05e1601", size = 258784, upload-time = "2026-01-26T02:45:30.503Z" }, + { url = "https://files.pythonhosted.org/packages/4d/c1/11f664f14d525e4a1b5327a82d4de61a1db604ab34c6603bb3c2cc63ad34/multidict-6.7.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:93b1818e4a6e0930454f0f2af7dfce69307ca03cdcfb3739bf4d91241967b6c1", size = 251980, upload-time = "2026-01-26T02:45:32.603Z" }, + { url = "https://files.pythonhosted.org/packages/e1/9f/75a9ac888121d0c5bbd4ecf4eead45668b1766f6baabfb3b7f66a410e231/multidict-6.7.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f33dc2a3abe9249ea5d8360f969ec7f4142e7ac45ee7014d8f8d5acddf178b7b", size = 243602, upload-time = "2026-01-26T02:45:34.043Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e7/50bf7b004cc8525d80dbbbedfdc7aed3e4c323810890be4413e589074032/multidict-6.7.1-cp314-cp314-win32.whl", hash = "sha256:3ab8b9d8b75aef9df299595d5388b14530839f6422333357af1339443cff777d", size = 40930, upload-time = "2026-01-26T02:45:36.278Z" }, + { url = "https://files.pythonhosted.org/packages/e0/bf/52f25716bbe93745595800f36fb17b73711f14da59ed0bb2eba141bc9f0f/multidict-6.7.1-cp314-cp314-win_amd64.whl", hash = "sha256:5e01429a929600e7dab7b166062d9bb54a5eed752384c7384c968c2afab8f50f", size = 45074, upload-time = "2026-01-26T02:45:37.546Z" }, + { url = "https://files.pythonhosted.org/packages/97/ab/22803b03285fa3a525f48217963da3a65ae40f6a1b6f6cf2768879e208f9/multidict-6.7.1-cp314-cp314-win_arm64.whl", hash = "sha256:4885cb0e817aef5d00a2e8451d4665c1808378dc27c2705f1bf4ef8505c0d2e5", size = 42471, upload-time = "2026-01-26T02:45:38.889Z" }, + { url = "https://files.pythonhosted.org/packages/e0/6d/f9293baa6146ba9507e360ea0292b6422b016907c393e2f63fc40ab7b7b5/multidict-6.7.1-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:0458c978acd8e6ea53c81eefaddbbee9c6c5e591f41b3f5e8e194780fe026581", size = 82401, upload-time = "2026-01-26T02:45:40.254Z" }, + { url = "https://files.pythonhosted.org/packages/7a/68/53b5494738d83558d87c3c71a486504d8373421c3e0dbb6d0db48ad42ee0/multidict-6.7.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:c0abd12629b0af3cf590982c0b413b1e7395cd4ec026f30986818ab95bfaa94a", size = 48143, upload-time = "2026-01-26T02:45:41.635Z" }, + { url = "https://files.pythonhosted.org/packages/37/e8/5284c53310dcdc99ce5d66563f6e5773531a9b9fe9ec7a615e9bc306b05f/multidict-6.7.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:14525a5f61d7d0c94b368a42cff4c9a4e7ba2d52e2672a7b23d84dc86fb02b0c", size = 46507, upload-time = "2026-01-26T02:45:42.99Z" }, + { url = "https://files.pythonhosted.org/packages/e4/fc/6800d0e5b3875568b4083ecf5f310dcf91d86d52573160834fb4bfcf5e4f/multidict-6.7.1-cp314-cp314t-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:17307b22c217b4cf05033dabefe68255a534d637c6c9b0cc8382718f87be4262", size = 239358, upload-time = "2026-01-26T02:45:44.376Z" }, + { url = "https://files.pythonhosted.org/packages/41/75/4ad0973179361cdf3a113905e6e088173198349131be2b390f9fa4da5fc6/multidict-6.7.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7a7e590ff876a3eaf1c02a4dfe0724b6e69a9e9de6d8f556816f29c496046e59", size = 246884, upload-time = "2026-01-26T02:45:47.167Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/095bb28b5da139bd41fb9a5d5caff412584f377914bd8787c2aa98717130/multidict-6.7.1-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:5fa6a95dfee63893d80a34758cd0e0c118a30b8dcb46372bf75106c591b77889", size = 225878, upload-time = "2026-01-26T02:45:48.698Z" }, + { url = "https://files.pythonhosted.org/packages/07/d0/c0a72000243756e8f5a277b6b514fa005f2c73d481b7d9e47cd4568aa2e4/multidict-6.7.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a0543217a6a017692aa6ae5cc39adb75e587af0f3a82288b1492eb73dd6cc2a4", size = 253542, upload-time = "2026-01-26T02:45:50.164Z" }, + { url = "https://files.pythonhosted.org/packages/c0/6b/f69da15289e384ecf2a68837ec8b5ad8c33e973aa18b266f50fe55f24b8c/multidict-6.7.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f99fe611c312b3c1c0ace793f92464d8cd263cc3b26b5721950d977b006b6c4d", size = 252403, upload-time = "2026-01-26T02:45:51.779Z" }, + { url = "https://files.pythonhosted.org/packages/a2/76/b9669547afa5a1a25cd93eaca91c0da1c095b06b6d2d8ec25b713588d3a1/multidict-6.7.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9004d8386d133b7e6135679424c91b0b854d2d164af6ea3f289f8f2761064609", size = 244889, upload-time = "2026-01-26T02:45:53.27Z" }, + { url = "https://files.pythonhosted.org/packages/7e/a9/a50d2669e506dad33cfc45b5d574a205587b7b8a5f426f2fbb2e90882588/multidict-6.7.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e628ef0e6859ffd8273c69412a2465c4be4a9517d07261b33334b5ec6f3c7489", size = 241982, upload-time = "2026-01-26T02:45:54.919Z" }, + { url = "https://files.pythonhosted.org/packages/c5/bb/1609558ad8b456b4827d3c5a5b775c93b87878fd3117ed3db3423dfbce1b/multidict-6.7.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:841189848ba629c3552035a6a7f5bf3b02eb304e9fea7492ca220a8eda6b0e5c", size = 232415, upload-time = "2026-01-26T02:45:56.981Z" }, + { url = "https://files.pythonhosted.org/packages/d8/59/6f61039d2aa9261871e03ab9dc058a550d240f25859b05b67fd70f80d4b3/multidict-6.7.1-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:ce1bbd7d780bb5a0da032e095c951f7014d6b0a205f8318308140f1a6aba159e", size = 240337, upload-time = "2026-01-26T02:45:58.698Z" }, + { url = "https://files.pythonhosted.org/packages/a1/29/fdc6a43c203890dc2ae9249971ecd0c41deaedfe00d25cb6564b2edd99eb/multidict-6.7.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:b26684587228afed0d50cf804cc71062cc9c1cdf55051c4c6345d372947b268c", size = 248788, upload-time = "2026-01-26T02:46:00.862Z" }, + { url = "https://files.pythonhosted.org/packages/a9/14/a153a06101323e4cf086ecee3faadba52ff71633d471f9685c42e3736163/multidict-6.7.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:9f9af11306994335398293f9958071019e3ab95e9a707dc1383a35613f6abcb9", size = 242842, upload-time = "2026-01-26T02:46:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/41/5f/604ae839e64a4a6efc80db94465348d3b328ee955e37acb24badbcd24d83/multidict-6.7.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b4938326284c4f1224178a560987b6cf8b4d38458b113d9b8c1db1a836e640a2", size = 240237, upload-time = "2026-01-26T02:46:05.898Z" }, + { url = "https://files.pythonhosted.org/packages/5f/60/c3a5187bf66f6fb546ff4ab8fb5a077cbdd832d7b1908d4365c7f74a1917/multidict-6.7.1-cp314-cp314t-win32.whl", hash = "sha256:98655c737850c064a65e006a3df7c997cd3b220be4ec8fe26215760b9697d4d7", size = 48008, upload-time = "2026-01-26T02:46:07.468Z" }, + { url = "https://files.pythonhosted.org/packages/0c/f7/addf1087b860ac60e6f382240f64fb99f8bfb532bb06f7c542b83c29ca61/multidict-6.7.1-cp314-cp314t-win_amd64.whl", hash = "sha256:497bde6223c212ba11d462853cfa4f0ae6ef97465033e7dc9940cdb3ab5b48e5", size = 53542, upload-time = "2026-01-26T02:46:08.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/81/4629d0aa32302ef7b2ec65c75a728cc5ff4fa410c50096174c1632e70b3e/multidict-6.7.1-cp314-cp314t-win_arm64.whl", hash = "sha256:2bbd113e0d4af5db41d5ebfe9ccaff89de2120578164f86a5d17d5a576d1e5b2", size = 44719, upload-time = "2026-01-26T02:46:11.146Z" }, + { url = "https://files.pythonhosted.org/packages/9e/ee/74525ebe3eb5fddcd6735fc03cbea3feeed4122b53bc798ac32d297ac9ae/multidict-6.7.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:65573858d27cdeaca41893185677dc82395159aa28875a8867af66532d413a8f", size = 77107, upload-time = "2026-01-26T02:46:12.608Z" }, + { url = "https://files.pythonhosted.org/packages/f0/9a/ce8744e777a74b3050b1bf56be3eed1053b3457302ea055f1ea437200a23/multidict-6.7.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:c524c6fb8fc342793708ab111c4dbc90ff9abd568de220432500e47e990c0358", size = 44943, upload-time = "2026-01-26T02:46:14.016Z" }, + { url = "https://files.pythonhosted.org/packages/83/9c/1d2a283d9c6f31e260cb6c2fccadc3edcf6c4c14ee0929cd2af4d2606dd7/multidict-6.7.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:aa23b001d968faef416ff70dc0f1ab045517b9b42a90edd3e9bcdb06479e31d5", size = 44603, upload-time = "2026-01-26T02:46:15.391Z" }, + { url = "https://files.pythonhosted.org/packages/87/9d/3b186201671583d8e8d6d79c07481a5aafd0ba7575e3d8566baec80c1e82/multidict-6.7.1-cp39-cp39-manylinux1_i686.manylinux_2_28_i686.manylinux_2_5_i686.whl", hash = "sha256:6704fa2b7453b2fb121740555fa1ee20cd98c4d011120caf4d2b8d4e7c76eec0", size = 240573, upload-time = "2026-01-26T02:46:16.783Z" }, + { url = "https://files.pythonhosted.org/packages/42/7d/a52f5d4d0754311d1ac78478e34dff88de71259a8585e05ee14e5f877caf/multidict-6.7.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:121a34e5bfa410cdf2c8c49716de160de3b1dbcd86b49656f5681e4543bcd1a8", size = 240106, upload-time = "2026-01-26T02:46:18.432Z" }, + { url = "https://files.pythonhosted.org/packages/84/9f/d80118e6c30ff55b7d171bdc5520aad4b9626e657520b8d7c8ca8c2fad12/multidict-6.7.1-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:026d264228bcd637d4e060844e39cdc60f86c479e463d49075dedc21b18fbbe0", size = 219418, upload-time = "2026-01-26T02:46:20.526Z" }, + { url = "https://files.pythonhosted.org/packages/c7/bd/896e60b3457f194de77c7de64f9acce9f75da0518a5230ce1df534f6747b/multidict-6.7.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:0e697826df7eb63418ee190fd06ce9f1803593bb4b9517d08c60d9b9a7f69d8f", size = 252124, upload-time = "2026-01-26T02:46:22.157Z" }, + { url = "https://files.pythonhosted.org/packages/f4/de/ba6b30447c36a37078d0ba604aa12c1a52887af0c355236ca6e0a9d5286f/multidict-6.7.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:bb08271280173720e9fea9ede98e5231defcbad90f1624bea26f32ec8a956e2f", size = 249402, upload-time = "2026-01-26T02:46:23.718Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b2/50a383c96230e432895a2fd3bcfe1b65785899598259d871d5de6b93180c/multidict-6.7.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c6b3228e1d80af737b72925ce5fb4daf5a335e49cd7ab77ed7b9fdfbf58c526e", size = 240346, upload-time = "2026-01-26T02:46:25.393Z" }, + { url = "https://files.pythonhosted.org/packages/89/37/16d391fd8da544b1489306e38a46785fa41dd0f0ef766837ed7d4676dde0/multidict-6.7.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:3943debf0fbb57bdde5901695c11094a9a36723e5c03875f87718ee15ca2f4d2", size = 237010, upload-time = "2026-01-26T02:46:27.408Z" }, + { url = "https://files.pythonhosted.org/packages/b0/24/3152ee026eda86d5d3e3685182911e6951af7a016579da931080ce6ac9ad/multidict-6.7.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:98c5787b0a0d9a41d9311eae44c3b76e6753def8d8870ab501320efe75a6a5f8", size = 232018, upload-time = "2026-01-26T02:46:29.941Z" }, + { url = "https://files.pythonhosted.org/packages/9c/1f/48d3c27a72be7fd23a55d8847193c459959bf35a5bb5844530dab00b739b/multidict-6.7.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:08ccb2a6dc72009093ebe7f3f073e5ec5964cba9a706fa94b1a1484039b87941", size = 241498, upload-time = "2026-01-26T02:46:32.052Z" }, + { url = "https://files.pythonhosted.org/packages/1a/45/413643ae2952d0decdf6c1250f86d08a43e143271441e81027e38d598bd7/multidict-6.7.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb351f72c26dc9abe338ca7294661aa22969ad8ffe7ef7d5541d19f368dc854a", size = 247957, upload-time = "2026-01-26T02:46:33.666Z" }, + { url = "https://files.pythonhosted.org/packages/50/f8/f1d0ac23df15e0470776388bdb261506f63af1f81d28bacb5e262d6e12b6/multidict-6.7.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:ac1c665bad8b5d762f5f85ebe4d94130c26965f11de70c708c75671297c776de", size = 241651, upload-time = "2026-01-26T02:46:35.7Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c9/1a2a18f383cf129add66b6c36b75c3911a7ba95cf26cb141482de085cc12/multidict-6.7.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:1fa6609d0364f4f6f58351b4659a1f3e0e898ba2a8c5cac04cb2c7bc556b0bc5", size = 236371, upload-time = "2026-01-26T02:46:37.37Z" }, + { url = "https://files.pythonhosted.org/packages/bb/aa/77d87e3fca31325b87e0eb72d5fe9a7472dcb51391a42df7ac1f3842f6c0/multidict-6.7.1-cp39-cp39-win32.whl", hash = "sha256:6f77ce314a29263e67adadc7e7c1bc699fcb3a305059ab973d038f87caa42ed0", size = 41426, upload-time = "2026-01-26T02:46:39.026Z" }, + { url = "https://files.pythonhosted.org/packages/e3/b3/e8863e6a2da15a9d7e98976ff402e871b7352c76566df6c18d0378e0d9cf/multidict-6.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:f537b55778cd3cbee430abe3131255d3a78202e0f9ea7ffc6ada893a4bcaeea4", size = 46180, upload-time = "2026-01-26T02:46:40.422Z" }, + { url = "https://files.pythonhosted.org/packages/93/d3/dd4fa951ad5b5fa216bf30054d705683d13405eea7459833d78f31b74c9c/multidict-6.7.1-cp39-cp39-win_arm64.whl", hash = "sha256:749aa54f578f2e5f439538706a475aa844bfa8ef75854b1401e6e528e4937cf9", size = 43231, upload-time = "2026-01-26T02:46:41.945Z" }, + { url = "https://files.pythonhosted.org/packages/81/08/7036c080d7117f28a4af526d794aab6a84463126db031b007717c1a6676e/multidict-6.7.1-py3-none-any.whl", hash = "sha256:55d97cc6dae627efa6a6e548885712d4864b81110ac76fa4e534c03819fa4a56", size = 12319, upload-time = "2026-01-26T02:46:44.004Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/de/eb/b83e75f4c820c4247a58580ef86fcd35165028f191e7e1ba57128c52782d/mypy-1.19.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:06e6170bd5836770e8104c8fdd58e5e725cfeb309f0a6c681a811f557e97eac1", size = 13199744, upload-time = "2025-12-15T05:03:30.823Z" }, + { url = "https://files.pythonhosted.org/packages/94/28/52785ab7bfa165f87fcbb61547a93f98bb20e7f82f90f165a1f69bce7b3d/mypy-1.19.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:804bd67b8054a85447c8954215a906d6eff9cabeabe493fb6334b24f4bfff718", size = 12215815, upload-time = "2025-12-15T05:02:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/0a/c6/bdd60774a0dbfb05122e3e925f2e9e846c009e479dcec4821dad881f5b52/mypy-1.19.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:21761006a7f497cb0d4de3d8ef4ca70532256688b0523eee02baf9eec895e27b", size = 12740047, upload-time = "2025-12-15T05:03:33.168Z" }, + { url = "https://files.pythonhosted.org/packages/32/2a/66ba933fe6c76bd40d1fe916a83f04fed253152f451a877520b3c4a5e41e/mypy-1.19.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:28902ee51f12e0f19e1e16fbe2f8f06b6637f482c459dd393efddd0ec7f82045", size = 13601998, upload-time = "2025-12-15T05:03:13.056Z" }, + { url = "https://files.pythonhosted.org/packages/e3/da/5055c63e377c5c2418760411fd6a63ee2b96cf95397259038756c042574f/mypy-1.19.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:481daf36a4c443332e2ae9c137dfee878fcea781a2e3f895d54bd3002a900957", size = 13807476, upload-time = "2025-12-15T05:03:17.977Z" }, + { url = "https://files.pythonhosted.org/packages/cd/09/4ebd873390a063176f06b0dbf1f7783dd87bd120eae7727fa4ae4179b685/mypy-1.19.1-cp314-cp314-win_amd64.whl", hash = "sha256:8bb5c6f6d043655e055be9b542aa5f3bdd30e4f3589163e85f93f3640060509f", size = 10281872, upload-time = "2025-12-15T05:03:05.549Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, + { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, + { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nh3" +version = "0.3.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cc/37/ab55eb2b05e334ff9a1ad52c556ace1f9c20a3f63613a165d384d5387657/nh3-0.3.3.tar.gz", hash = "sha256:185ed41b88c910b9ca8edc89ca3b4be688a12cb9de129d84befa2f74a0039fee", size = 18968, upload-time = "2026-02-14T09:35:15.664Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e7/a4/834f0ebd80844ce67e1bdb011d6f844f61cdb4c1d7cdc56a982bc054cc00/nh3-0.3.3-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:21b058cd20d9f0919421a820a2843fdb5e1749c0bf57a6247ab8f4ba6723c9fc", size = 1428680, upload-time = "2026-02-14T09:34:33.015Z" }, + { url = "https://files.pythonhosted.org/packages/7f/1a/a7d72e750f74c6b71befbeebc4489579fe783466889d41f32e34acde0b6b/nh3-0.3.3-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4400a73c2a62859e769f9d36d1b5a7a5c65c4179d1dddd2f6f3095b2db0cbfc", size = 799003, upload-time = "2026-02-14T09:34:35.108Z" }, + { url = "https://files.pythonhosted.org/packages/58/d5/089eb6d65da139dc2223b83b2627e00872eccb5e1afdf5b1d76eb6ad3fcc/nh3-0.3.3-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1ef87f8e916321a88b45f2d597f29bd56e560ed4568a50f0f1305afab86b7189", size = 846818, upload-time = "2026-02-14T09:34:37Z" }, + { url = "https://files.pythonhosted.org/packages/9b/c6/44a0b65fc7b213a3a725f041ef986534b100e58cd1a2e00f0fd3c9603893/nh3-0.3.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:a446eae598987f49ee97ac2f18eafcce4e62e7574bd1eb23782e4702e54e217d", size = 1012537, upload-time = "2026-02-14T09:34:38.515Z" }, + { url = "https://files.pythonhosted.org/packages/94/3a/91bcfcc0a61b286b8b25d39e288b9c0ba91c3290d402867d1cd705169844/nh3-0.3.3-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:0d5eb734a78ac364af1797fef718340a373f626a9ff6b4fb0b4badf7927e7b81", size = 1095435, upload-time = "2026-02-14T09:34:40.022Z" }, + { url = "https://files.pythonhosted.org/packages/fd/fd/4617a19d80cf9f958e65724ff5e97bc2f76f2f4c5194c740016606c87bd1/nh3-0.3.3-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:92a958e6f6d0100e025a5686aafd67e3c98eac67495728f8bb64fbeb3e474493", size = 1056344, upload-time = "2026-02-14T09:34:41.469Z" }, + { url = "https://files.pythonhosted.org/packages/bd/7d/5bcbbc56e71b7dda7ef1d6008098da9c5426d6334137ef32bb2b9c496984/nh3-0.3.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:9ed40cf8449a59a03aa465114fedce1ff7ac52561688811d047917cc878b19ca", size = 1034533, upload-time = "2026-02-14T09:34:43.313Z" }, + { url = "https://files.pythonhosted.org/packages/3f/9c/054eff8a59a8b23b37f0f4ac84cdd688ee84cf5251664c0e14e5d30a8a67/nh3-0.3.3-cp314-cp314t-win32.whl", hash = "sha256:b50c3770299fb2a7c1113751501e8878d525d15160a4c05194d7fe62b758aad8", size = 608305, upload-time = "2026-02-14T09:34:44.622Z" }, + { url = "https://files.pythonhosted.org/packages/d7/b0/64667b8d522c7b859717a02b1a66ba03b529ca1df623964e598af8db1ed5/nh3-0.3.3-cp314-cp314t-win_amd64.whl", hash = "sha256:21a63ccb18ddad3f784bb775955839b8b80e347e597726f01e43ca1abcc5c808", size = 620633, upload-time = "2026-02-14T09:34:46.069Z" }, + { url = "https://files.pythonhosted.org/packages/91/b5/ae9909e4ddfd86ee076c4d6d62ba69e9b31061da9d2f722936c52df8d556/nh3-0.3.3-cp314-cp314t-win_arm64.whl", hash = "sha256:f508ddd4e2433fdcb78c790fc2d24e3a349ba775e5fa904af89891321d4844a3", size = 607027, upload-time = "2026-02-14T09:34:47.91Z" }, + { url = "https://files.pythonhosted.org/packages/13/3e/aef8cf8e0419b530c95e96ae93a5078e9b36c1e6613eeb1df03a80d5194e/nh3-0.3.3-cp38-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e8ee96156f7dfc6e30ecda650e480c5ae0a7d38f0c6fafc3c1c655e2500421d9", size = 1448640, upload-time = "2026-02-14T09:34:49.316Z" }, + { url = "https://files.pythonhosted.org/packages/ca/43/d2011a4f6c0272cb122eeff40062ee06bb2b6e57eabc3a5e057df0d582df/nh3-0.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45fe0d6a607264910daec30360c8a3b5b1500fd832d21b2da608256287bcb92d", size = 839405, upload-time = "2026-02-14T09:34:50.779Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f3/965048510c1caf2a34ed04411a46a04a06eb05563cd06f1aa57b71eb2bc8/nh3-0.3.3-cp38-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5bc1d4b30ba1ba896669d944b6003630592665974bd11a3dc2f661bde92798a7", size = 825849, upload-time = "2026-02-14T09:34:52.622Z" }, + { url = "https://files.pythonhosted.org/packages/78/99/b4bbc6ad16329d8db2c2c320423f00b549ca3b129c2b2f9136be2606dbb0/nh3-0.3.3-cp38-abi3-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:f433a2dd66545aad4a720ad1b2150edcdca75bfff6f4e6f378ade1ec138d5e77", size = 1068303, upload-time = "2026-02-14T09:34:54.179Z" }, + { url = "https://files.pythonhosted.org/packages/3f/34/3420d97065aab1b35f3e93ce9c96c8ebd423ce86fe84dee3126790421a2a/nh3-0.3.3-cp38-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52e973cb742e95b9ae1b35822ce23992428750f4b46b619fe86eba4205255b30", size = 1029316, upload-time = "2026-02-14T09:34:56.186Z" }, + { url = "https://files.pythonhosted.org/packages/f1/9a/99eda757b14e596fdb2ca5f599a849d9554181aa899274d0d183faef4493/nh3-0.3.3-cp38-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4c730617bdc15d7092dcc0469dc2826b914c8f874996d105b4bc3842a41c1cd9", size = 919944, upload-time = "2026-02-14T09:34:57.886Z" }, + { url = "https://files.pythonhosted.org/packages/6f/84/c0dc75c7fb596135f999e59a410d9f45bdabb989f1cb911f0016d22b747b/nh3-0.3.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e98fa3dbfd54e25487e36ba500bc29bca3a4cab4ffba18cfb1a35a2d02624297", size = 811461, upload-time = "2026-02-14T09:34:59.65Z" }, + { url = "https://files.pythonhosted.org/packages/7e/ec/b1bf57cab6230eec910e4863528dc51dcf21b57aaf7c88ee9190d62c9185/nh3-0.3.3-cp38-abi3-manylinux_2_31_riscv64.whl", hash = "sha256:3a62b8ae7c235481715055222e54c682422d0495a5c73326807d4e44c5d14691", size = 840360, upload-time = "2026-02-14T09:35:01.444Z" }, + { url = "https://files.pythonhosted.org/packages/37/5e/326ae34e904dde09af1de51219a611ae914111f0970f2f111f4f0188f57e/nh3-0.3.3-cp38-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc305a2264868ec8fa16548296f803d8fd9c1fa66cd28b88b605b1bd06667c0b", size = 859872, upload-time = "2026-02-14T09:35:03.348Z" }, + { url = "https://files.pythonhosted.org/packages/09/38/7eba529ce17ab4d3790205da37deabb4cb6edcba15f27b8562e467f2fc97/nh3-0.3.3-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:90126a834c18af03bfd6ff9a027bfa6bbf0e238527bc780a24de6bd7cc1041e2", size = 1023550, upload-time = "2026-02-14T09:35:04.829Z" }, + { url = "https://files.pythonhosted.org/packages/05/a2/556fdecd37c3681b1edee2cf795a6799c6ed0a5551b2822636960d7e7651/nh3-0.3.3-cp38-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:24769a428e9e971e4ccfb24628f83aaa7dc3c8b41b130c8ddc1835fa1c924489", size = 1105212, upload-time = "2026-02-14T09:35:06.821Z" }, + { url = "https://files.pythonhosted.org/packages/dd/e3/5db0b0ad663234967d83702277094687baf7c498831a2d3ad3451c11770f/nh3-0.3.3-cp38-abi3-musllinux_1_2_i686.whl", hash = "sha256:b7a18ee057761e455d58b9d31445c3e4b2594cff4ddb84d2e331c011ef46f462", size = 1069970, upload-time = "2026-02-14T09:35:08.504Z" }, + { url = "https://files.pythonhosted.org/packages/79/b2/2ea21b79c6e869581ce5f51549b6e185c4762233591455bf2a326fb07f3b/nh3-0.3.3-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:5a4b2c1f3e6f3cbe7048e17f4fefad3f8d3e14cc0fd08fb8599e0d5653f6b181", size = 1047588, upload-time = "2026-02-14T09:35:09.911Z" }, + { url = "https://files.pythonhosted.org/packages/e2/92/2e434619e658c806d9c096eed2cdff9a883084299b7b19a3f0824eb8e63d/nh3-0.3.3-cp38-abi3-win32.whl", hash = "sha256:e974850b131fdffa75e7ad8e0d9c7a855b96227b093417fdf1bd61656e530f37", size = 616179, upload-time = "2026-02-14T09:35:11.366Z" }, + { url = "https://files.pythonhosted.org/packages/73/88/1ce287ef8649dc51365b5094bd3713b76454838140a32ab4f8349973883c/nh3-0.3.3-cp38-abi3-win_amd64.whl", hash = "sha256:2efd17c0355d04d39e6d79122b42662277ac10a17ea48831d90b46e5ef7e4fc0", size = 631159, upload-time = "2026-02-14T09:35:12.77Z" }, + { url = "https://files.pythonhosted.org/packages/31/f1/b4835dbde4fb06f29db89db027576d6014081cd278d9b6751facc3e69e43/nh3-0.3.3-cp38-abi3-win_arm64.whl", hash = "sha256:b838e619f483531483d26d889438e53a880510e832d2aafe73f93b7b1ac2bce2", size = 616645, upload-time = "2026-02-14T09:35:14.062Z" }, +] + +[[package]] +name = "numpy" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/a9/75/10dd1f8116a8b796cb2c737b674e02d02e80454bda953fa7e65d8c12b016/numpy-2.0.2.tar.gz", hash = "sha256:883c987dee1880e2a864ab0dc9892292582510604156762362d9326444636e78", size = 18902015, upload-time = "2024-08-26T20:19:40.945Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/21/91/3495b3237510f79f5d81f2508f9f13fea78ebfdf07538fc7444badda173d/numpy-2.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:51129a29dbe56f9ca83438b706e2e69a39892b5eda6cedcb6b0c9fdc9b0d3ece", size = 21165245, upload-time = "2024-08-26T20:04:14.625Z" }, + { url = "https://files.pythonhosted.org/packages/05/33/26178c7d437a87082d11019292dce6d3fe6f0e9026b7b2309cbf3e489b1d/numpy-2.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f15975dfec0cf2239224d80e32c3170b1d168335eaedee69da84fbe9f1f9cd04", size = 13738540, upload-time = "2024-08-26T20:04:36.784Z" }, + { url = "https://files.pythonhosted.org/packages/ec/31/cc46e13bf07644efc7a4bf68df2df5fb2a1a88d0cd0da9ddc84dc0033e51/numpy-2.0.2-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8c5713284ce4e282544c68d1c3b2c7161d38c256d2eefc93c1d683cf47683e66", size = 5300623, upload-time = "2024-08-26T20:04:46.491Z" }, + { url = "https://files.pythonhosted.org/packages/6e/16/7bfcebf27bb4f9d7ec67332ffebee4d1bf085c84246552d52dbb548600e7/numpy-2.0.2-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:becfae3ddd30736fe1889a37f1f580e245ba79a5855bff5f2a29cb3ccc22dd7b", size = 6901774, upload-time = "2024-08-26T20:04:58.173Z" }, + { url = "https://files.pythonhosted.org/packages/f9/a3/561c531c0e8bf082c5bef509d00d56f82e0ea7e1e3e3a7fc8fa78742a6e5/numpy-2.0.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2da5960c3cf0df7eafefd806d4e612c5e19358de82cb3c343631188991566ccd", size = 13907081, upload-time = "2024-08-26T20:05:19.098Z" }, + { url = "https://files.pythonhosted.org/packages/fa/66/f7177ab331876200ac7563a580140643d1179c8b4b6a6b0fc9838de2a9b8/numpy-2.0.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:496f71341824ed9f3d2fd36cf3ac57ae2e0165c143b55c3a035ee219413f3318", size = 19523451, upload-time = "2024-08-26T20:05:47.479Z" }, + { url = "https://files.pythonhosted.org/packages/25/7f/0b209498009ad6453e4efc2c65bcdf0ae08a182b2b7877d7ab38a92dc542/numpy-2.0.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a61ec659f68ae254e4d237816e33171497e978140353c0c2038d46e63282d0c8", size = 19927572, upload-time = "2024-08-26T20:06:17.137Z" }, + { url = "https://files.pythonhosted.org/packages/3e/df/2619393b1e1b565cd2d4c4403bdd979621e2c4dea1f8532754b2598ed63b/numpy-2.0.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d731a1c6116ba289c1e9ee714b08a8ff882944d4ad631fd411106a30f083c326", size = 14400722, upload-time = "2024-08-26T20:06:39.16Z" }, + { url = "https://files.pythonhosted.org/packages/22/ad/77e921b9f256d5da36424ffb711ae79ca3f451ff8489eeca544d0701d74a/numpy-2.0.2-cp310-cp310-win32.whl", hash = "sha256:984d96121c9f9616cd33fbd0618b7f08e0cfc9600a7ee1d6fd9b239186d19d97", size = 6472170, upload-time = "2024-08-26T20:06:50.361Z" }, + { url = "https://files.pythonhosted.org/packages/10/05/3442317535028bc29cf0c0dd4c191a4481e8376e9f0db6bcf29703cadae6/numpy-2.0.2-cp310-cp310-win_amd64.whl", hash = "sha256:c7b0be4ef08607dd04da4092faee0b86607f111d5ae68036f16cc787e250a131", size = 15905558, upload-time = "2024-08-26T20:07:13.881Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cf/034500fb83041aa0286e0fb16e7c76e5c8b67c0711bb6e9e9737a717d5fe/numpy-2.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:49ca4decb342d66018b01932139c0961a8f9ddc7589611158cb3c27cbcf76448", size = 21169137, upload-time = "2024-08-26T20:07:45.345Z" }, + { url = "https://files.pythonhosted.org/packages/4a/d9/32de45561811a4b87fbdee23b5797394e3d1504b4a7cf40c10199848893e/numpy-2.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:11a76c372d1d37437857280aa142086476136a8c0f373b2e648ab2c8f18fb195", size = 13703552, upload-time = "2024-08-26T20:08:06.666Z" }, + { url = "https://files.pythonhosted.org/packages/c1/ca/2f384720020c7b244d22508cb7ab23d95f179fcfff33c31a6eeba8d6c512/numpy-2.0.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:807ec44583fd708a21d4a11d94aedf2f4f3c3719035c76a2bbe1fe8e217bdc57", size = 5298957, upload-time = "2024-08-26T20:08:15.83Z" }, + { url = "https://files.pythonhosted.org/packages/0e/78/a3e4f9fb6aa4e6fdca0c5428e8ba039408514388cf62d89651aade838269/numpy-2.0.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8cafab480740e22f8d833acefed5cc87ce276f4ece12fdaa2e8903db2f82897a", size = 6905573, upload-time = "2024-08-26T20:08:27.185Z" }, + { url = "https://files.pythonhosted.org/packages/a0/72/cfc3a1beb2caf4efc9d0b38a15fe34025230da27e1c08cc2eb9bfb1c7231/numpy-2.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a15f476a45e6e5a3a79d8a14e62161d27ad897381fecfa4a09ed5322f2085669", size = 13914330, upload-time = "2024-08-26T20:08:48.058Z" }, + { url = "https://files.pythonhosted.org/packages/ba/a8/c17acf65a931ce551fee11b72e8de63bf7e8a6f0e21add4c937c83563538/numpy-2.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13e689d772146140a252c3a28501da66dfecd77490b498b168b501835041f951", size = 19534895, upload-time = "2024-08-26T20:09:16.536Z" }, + { url = "https://files.pythonhosted.org/packages/ba/86/8767f3d54f6ae0165749f84648da9dcc8cd78ab65d415494962c86fac80f/numpy-2.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:9ea91dfb7c3d1c56a0e55657c0afb38cf1eeae4544c208dc465c3c9f3a7c09f9", size = 19937253, upload-time = "2024-08-26T20:09:46.263Z" }, + { url = "https://files.pythonhosted.org/packages/df/87/f76450e6e1c14e5bb1eae6836478b1028e096fd02e85c1c37674606ab752/numpy-2.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c1c9307701fec8f3f7a1e6711f9089c06e6284b3afbbcd259f7791282d660a15", size = 14414074, upload-time = "2024-08-26T20:10:08.483Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/0f0f328e1e59f73754f06e1adfb909de43726d4f24c6a3f8805f34f2b0fa/numpy-2.0.2-cp311-cp311-win32.whl", hash = "sha256:a392a68bd329eafac5817e5aefeb39038c48b671afd242710b451e76090e81f4", size = 6470640, upload-time = "2024-08-26T20:10:19.732Z" }, + { url = "https://files.pythonhosted.org/packages/eb/57/3a3f14d3a759dcf9bf6e9eda905794726b758819df4663f217d658a58695/numpy-2.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:286cd40ce2b7d652a6f22efdfc6d1edf879440e53e76a75955bc0c826c7e64dc", size = 15910230, upload-time = "2024-08-26T20:10:43.413Z" }, + { url = "https://files.pythonhosted.org/packages/45/40/2e117be60ec50d98fa08c2f8c48e09b3edea93cfcabd5a9ff6925d54b1c2/numpy-2.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:df55d490dea7934f330006d0f81e8551ba6010a5bf035a249ef61a94f21c500b", size = 20895803, upload-time = "2024-08-26T20:11:13.916Z" }, + { url = "https://files.pythonhosted.org/packages/46/92/1b8b8dee833f53cef3e0a3f69b2374467789e0bb7399689582314df02651/numpy-2.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8df823f570d9adf0978347d1f926b2a867d5608f434a7cff7f7908c6570dcf5e", size = 13471835, upload-time = "2024-08-26T20:11:34.779Z" }, + { url = "https://files.pythonhosted.org/packages/7f/19/e2793bde475f1edaea6945be141aef6c8b4c669b90c90a300a8954d08f0a/numpy-2.0.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9a92ae5c14811e390f3767053ff54eaee3bf84576d99a2456391401323f4ec2c", size = 5038499, upload-time = "2024-08-26T20:11:43.902Z" }, + { url = "https://files.pythonhosted.org/packages/e3/ff/ddf6dac2ff0dd50a7327bcdba45cb0264d0e96bb44d33324853f781a8f3c/numpy-2.0.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:a842d573724391493a97a62ebbb8e731f8a5dcc5d285dfc99141ca15a3302d0c", size = 6633497, upload-time = "2024-08-26T20:11:55.09Z" }, + { url = "https://files.pythonhosted.org/packages/72/21/67f36eac8e2d2cd652a2e69595a54128297cdcb1ff3931cfc87838874bd4/numpy-2.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c05e238064fc0610c840d1cf6a13bf63d7e391717d247f1bf0318172e759e692", size = 13621158, upload-time = "2024-08-26T20:12:14.95Z" }, + { url = "https://files.pythonhosted.org/packages/39/68/e9f1126d757653496dbc096cb429014347a36b228f5a991dae2c6b6cfd40/numpy-2.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0123ffdaa88fa4ab64835dcbde75dcdf89c453c922f18dced6e27c90d1d0ec5a", size = 19236173, upload-time = "2024-08-26T20:12:44.049Z" }, + { url = "https://files.pythonhosted.org/packages/d1/e9/1f5333281e4ebf483ba1c888b1d61ba7e78d7e910fdd8e6499667041cc35/numpy-2.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:96a55f64139912d61de9137f11bf39a55ec8faec288c75a54f93dfd39f7eb40c", size = 19634174, upload-time = "2024-08-26T20:13:13.634Z" }, + { url = "https://files.pythonhosted.org/packages/71/af/a469674070c8d8408384e3012e064299f7a2de540738a8e414dcfd639996/numpy-2.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ec9852fb39354b5a45a80bdab5ac02dd02b15f44b3804e9f00c556bf24b4bded", size = 14099701, upload-time = "2024-08-26T20:13:34.851Z" }, + { url = "https://files.pythonhosted.org/packages/d0/3d/08ea9f239d0e0e939b6ca52ad403c84a2bce1bde301a8eb4888c1c1543f1/numpy-2.0.2-cp312-cp312-win32.whl", hash = "sha256:671bec6496f83202ed2d3c8fdc486a8fc86942f2e69ff0e986140339a63bcbe5", size = 6174313, upload-time = "2024-08-26T20:13:45.653Z" }, + { url = "https://files.pythonhosted.org/packages/b2/b5/4ac39baebf1fdb2e72585c8352c56d063b6126be9fc95bd2bb5ef5770c20/numpy-2.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:cfd41e13fdc257aa5778496b8caa5e856dc4896d4ccf01841daee1d96465467a", size = 15606179, upload-time = "2024-08-26T20:14:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/43/c1/41c8f6df3162b0c6ffd4437d729115704bd43363de0090c7f913cfbc2d89/numpy-2.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9059e10581ce4093f735ed23f3b9d283b9d517ff46009ddd485f1747eb22653c", size = 21169942, upload-time = "2024-08-26T20:14:40.108Z" }, + { url = "https://files.pythonhosted.org/packages/39/bc/fd298f308dcd232b56a4031fd6ddf11c43f9917fbc937e53762f7b5a3bb1/numpy-2.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:423e89b23490805d2a5a96fe40ec507407b8ee786d66f7328be214f9679df6dd", size = 13711512, upload-time = "2024-08-26T20:15:00.985Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/06d1aa3eeb1c614eda245c1ba4fb88c483bee6520d361641331872ac4b82/numpy-2.0.2-cp39-cp39-macosx_14_0_arm64.whl", hash = "sha256:2b2955fa6f11907cf7a70dab0d0755159bca87755e831e47932367fc8f2f2d0b", size = 5306976, upload-time = "2024-08-26T20:15:10.876Z" }, + { url = "https://files.pythonhosted.org/packages/2d/98/121996dcfb10a6087a05e54453e28e58694a7db62c5a5a29cee14c6e047b/numpy-2.0.2-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:97032a27bd9d8988b9a97a8c4d2c9f2c15a81f61e2f21404d7e8ef00cb5be729", size = 6906494, upload-time = "2024-08-26T20:15:22.055Z" }, + { url = "https://files.pythonhosted.org/packages/15/31/9dffc70da6b9bbf7968f6551967fc21156207366272c2a40b4ed6008dc9b/numpy-2.0.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1e795a8be3ddbac43274f18588329c72939870a16cae810c2b73461c40718ab1", size = 13912596, upload-time = "2024-08-26T20:15:42.452Z" }, + { url = "https://files.pythonhosted.org/packages/b9/14/78635daab4b07c0930c919d451b8bf8c164774e6a3413aed04a6d95758ce/numpy-2.0.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f26b258c385842546006213344c50655ff1555a9338e2e5e02a0756dc3e803dd", size = 19526099, upload-time = "2024-08-26T20:16:11.048Z" }, + { url = "https://files.pythonhosted.org/packages/26/4c/0eeca4614003077f68bfe7aac8b7496f04221865b3a5e7cb230c9d055afd/numpy-2.0.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5fec9451a7789926bcf7c2b8d187292c9f93ea30284802a0ab3f5be8ab36865d", size = 19932823, upload-time = "2024-08-26T20:16:40.171Z" }, + { url = "https://files.pythonhosted.org/packages/f1/46/ea25b98b13dccaebddf1a803f8c748680d972e00507cd9bc6dcdb5aa2ac1/numpy-2.0.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:9189427407d88ff25ecf8f12469d4d39d35bee1db5d39fc5c168c6f088a6956d", size = 14404424, upload-time = "2024-08-26T20:17:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/c8/a6/177dd88d95ecf07e722d21008b1b40e681a929eb9e329684d449c36586b2/numpy-2.0.2-cp39-cp39-win32.whl", hash = "sha256:905d16e0c60200656500c95b6b8dca5d109e23cb24abc701d41c02d74c6b3afa", size = 6476809, upload-time = "2024-08-26T20:17:13.553Z" }, + { url = "https://files.pythonhosted.org/packages/ea/2b/7fc9f4e7ae5b507c1a3a21f0f15ed03e794c1242ea8a242ac158beb56034/numpy-2.0.2-cp39-cp39-win_amd64.whl", hash = "sha256:a3f4ab0caa7f053f6797fcd4e1e25caee367db3112ef2b6ef82d749530768c73", size = 15911314, upload-time = "2024-08-26T20:17:36.72Z" }, + { url = "https://files.pythonhosted.org/packages/8f/3b/df5a870ac6a3be3a86856ce195ef42eec7ae50d2a202be1f5a4b3b340e14/numpy-2.0.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:7f0a0c6f12e07fa94133c8a67404322845220c06a9e80e85999afe727f7438b8", size = 21025288, upload-time = "2024-08-26T20:18:07.732Z" }, + { url = "https://files.pythonhosted.org/packages/2c/97/51af92f18d6f6f2d9ad8b482a99fb74e142d71372da5d834b3a2747a446e/numpy-2.0.2-pp39-pypy39_pp73-macosx_14_0_x86_64.whl", hash = "sha256:312950fdd060354350ed123c0e25a71327d3711584beaef30cdaa93320c392d4", size = 6762793, upload-time = "2024-08-26T20:18:19.125Z" }, + { url = "https://files.pythonhosted.org/packages/12/46/de1fbd0c1b5ccaa7f9a005b66761533e2f6a3e560096682683a223631fe9/numpy-2.0.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26df23238872200f63518dd2aa984cfca675d82469535dc7162dc2ee52d9dd5c", size = 19334885, upload-time = "2024-08-26T20:18:47.237Z" }, + { url = "https://files.pythonhosted.org/packages/cc/dc/d330a6faefd92b446ec0f0dfea4c3207bb1fef3c4771d19cf4543efd2c78/numpy-2.0.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a46288ec55ebbd58947d31d72be2c63cbf839f0a63b49cb755022310792a3385", size = 15828784, upload-time = "2024-08-26T20:19:11.19Z" }, +] + +[[package]] +name = "numpy" +version = "2.2.6" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/76/21/7d2a95e4bba9dc13d043ee156a356c0a8f0c6309dff6b21b4d71a073b8a8/numpy-2.2.6.tar.gz", hash = "sha256:e29554e2bef54a90aa5cc07da6ce955accb83f21ab5de01a62c8478897b264fd", size = 20276440, upload-time = "2025-05-17T22:38:04.611Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3e/ed6db5be21ce87955c0cbd3009f2803f59fa08df21b5df06862e2d8e2bdd/numpy-2.2.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:b412caa66f72040e6d268491a59f2c43bf03eb6c96dd8f0307829feb7fa2b6fb", size = 21165245, upload-time = "2025-05-17T21:27:58.555Z" }, + { url = "https://files.pythonhosted.org/packages/22/c2/4b9221495b2a132cc9d2eb862e21d42a009f5a60e45fc44b00118c174bff/numpy-2.2.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:8e41fd67c52b86603a91c1a505ebaef50b3314de0213461c7a6e99c9a3beff90", size = 14360048, upload-time = "2025-05-17T21:28:21.406Z" }, + { url = "https://files.pythonhosted.org/packages/fd/77/dc2fcfc66943c6410e2bf598062f5959372735ffda175b39906d54f02349/numpy-2.2.6-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:37e990a01ae6ec7fe7fa1c26c55ecb672dd98b19c3d0e1d1f326fa13cb38d163", size = 5340542, upload-time = "2025-05-17T21:28:30.931Z" }, + { url = "https://files.pythonhosted.org/packages/7a/4f/1cb5fdc353a5f5cc7feb692db9b8ec2c3d6405453f982435efc52561df58/numpy-2.2.6-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:5a6429d4be8ca66d889b7cf70f536a397dc45ba6faeb5f8c5427935d9592e9cf", size = 6878301, upload-time = "2025-05-17T21:28:41.613Z" }, + { url = "https://files.pythonhosted.org/packages/eb/17/96a3acd228cec142fcb8723bd3cc39c2a474f7dcf0a5d16731980bcafa95/numpy-2.2.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:efd28d4e9cd7d7a8d39074a4d44c63eda73401580c5c76acda2ce969e0a38e83", size = 14297320, upload-time = "2025-05-17T21:29:02.78Z" }, + { url = "https://files.pythonhosted.org/packages/b4/63/3de6a34ad7ad6646ac7d2f55ebc6ad439dbbf9c4370017c50cf403fb19b5/numpy-2.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc7b73d02efb0e18c000e9ad8b83480dfcd5dfd11065997ed4c6747470ae8915", size = 16801050, upload-time = "2025-05-17T21:29:27.675Z" }, + { url = "https://files.pythonhosted.org/packages/07/b6/89d837eddef52b3d0cec5c6ba0456c1bf1b9ef6a6672fc2b7873c3ec4e2e/numpy-2.2.6-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:74d4531beb257d2c3f4b261bfb0fc09e0f9ebb8842d82a7b4209415896adc680", size = 15807034, upload-time = "2025-05-17T21:29:51.102Z" }, + { url = "https://files.pythonhosted.org/packages/01/c8/dc6ae86e3c61cfec1f178e5c9f7858584049b6093f843bca541f94120920/numpy-2.2.6-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8fc377d995680230e83241d8a96def29f204b5782f371c532579b4f20607a289", size = 18614185, upload-time = "2025-05-17T21:30:18.703Z" }, + { url = "https://files.pythonhosted.org/packages/5b/c5/0064b1b7e7c89137b471ccec1fd2282fceaae0ab3a9550f2568782d80357/numpy-2.2.6-cp310-cp310-win32.whl", hash = "sha256:b093dd74e50a8cba3e873868d9e93a85b78e0daf2e98c6797566ad8044e8363d", size = 6527149, upload-time = "2025-05-17T21:30:29.788Z" }, + { url = "https://files.pythonhosted.org/packages/a3/dd/4b822569d6b96c39d1215dbae0582fd99954dcbcf0c1a13c61783feaca3f/numpy-2.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:f0fd6321b839904e15c46e0d257fdd101dd7f530fe03fd6359c1ea63738703f3", size = 12904620, upload-time = "2025-05-17T21:30:48.994Z" }, + { url = "https://files.pythonhosted.org/packages/da/a8/4f83e2aa666a9fbf56d6118faaaf5f1974d456b1823fda0a176eff722839/numpy-2.2.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f9f1adb22318e121c5c69a09142811a201ef17ab257a1e66ca3025065b7f53ae", size = 21176963, upload-time = "2025-05-17T21:31:19.36Z" }, + { url = "https://files.pythonhosted.org/packages/b3/2b/64e1affc7972decb74c9e29e5649fac940514910960ba25cd9af4488b66c/numpy-2.2.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c820a93b0255bc360f53eca31a0e676fd1101f673dda8da93454a12e23fc5f7a", size = 14406743, upload-time = "2025-05-17T21:31:41.087Z" }, + { url = "https://files.pythonhosted.org/packages/4a/9f/0121e375000b5e50ffdd8b25bf78d8e1a5aa4cca3f185d41265198c7b834/numpy-2.2.6-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3d70692235e759f260c3d837193090014aebdf026dfd167834bcba43e30c2a42", size = 5352616, upload-time = "2025-05-17T21:31:50.072Z" }, + { url = "https://files.pythonhosted.org/packages/31/0d/b48c405c91693635fbe2dcd7bc84a33a602add5f63286e024d3b6741411c/numpy-2.2.6-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:481b49095335f8eed42e39e8041327c05b0f6f4780488f61286ed3c01368d491", size = 6889579, upload-time = "2025-05-17T21:32:01.712Z" }, + { url = "https://files.pythonhosted.org/packages/52/b8/7f0554d49b565d0171eab6e99001846882000883998e7b7d9f0d98b1f934/numpy-2.2.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b64d8d4d17135e00c8e346e0a738deb17e754230d7e0810ac5012750bbd85a5a", size = 14312005, upload-time = "2025-05-17T21:32:23.332Z" }, + { url = "https://files.pythonhosted.org/packages/b3/dd/2238b898e51bd6d389b7389ffb20d7f4c10066d80351187ec8e303a5a475/numpy-2.2.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba10f8411898fc418a521833e014a77d3ca01c15b0c6cdcce6a0d2897e6dbbdf", size = 16821570, upload-time = "2025-05-17T21:32:47.991Z" }, + { url = "https://files.pythonhosted.org/packages/83/6c/44d0325722cf644f191042bf47eedad61c1e6df2432ed65cbe28509d404e/numpy-2.2.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:bd48227a919f1bafbdda0583705e547892342c26fb127219d60a5c36882609d1", size = 15818548, upload-time = "2025-05-17T21:33:11.728Z" }, + { url = "https://files.pythonhosted.org/packages/ae/9d/81e8216030ce66be25279098789b665d49ff19eef08bfa8cb96d4957f422/numpy-2.2.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9551a499bf125c1d4f9e250377c1ee2eddd02e01eac6644c080162c0c51778ab", size = 18620521, upload-time = "2025-05-17T21:33:39.139Z" }, + { url = "https://files.pythonhosted.org/packages/6a/fd/e19617b9530b031db51b0926eed5345ce8ddc669bb3bc0044b23e275ebe8/numpy-2.2.6-cp311-cp311-win32.whl", hash = "sha256:0678000bb9ac1475cd454c6b8c799206af8107e310843532b04d49649c717a47", size = 6525866, upload-time = "2025-05-17T21:33:50.273Z" }, + { url = "https://files.pythonhosted.org/packages/31/0a/f354fb7176b81747d870f7991dc763e157a934c717b67b58456bc63da3df/numpy-2.2.6-cp311-cp311-win_amd64.whl", hash = "sha256:e8213002e427c69c45a52bbd94163084025f533a55a59d6f9c5b820774ef3303", size = 12907455, upload-time = "2025-05-17T21:34:09.135Z" }, + { url = "https://files.pythonhosted.org/packages/82/5d/c00588b6cf18e1da539b45d3598d3557084990dcc4331960c15ee776ee41/numpy-2.2.6-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:41c5a21f4a04fa86436124d388f6ed60a9343a6f767fced1a8a71c3fbca038ff", size = 20875348, upload-time = "2025-05-17T21:34:39.648Z" }, + { url = "https://files.pythonhosted.org/packages/66/ee/560deadcdde6c2f90200450d5938f63a34b37e27ebff162810f716f6a230/numpy-2.2.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:de749064336d37e340f640b05f24e9e3dd678c57318c7289d222a8a2f543e90c", size = 14119362, upload-time = "2025-05-17T21:35:01.241Z" }, + { url = "https://files.pythonhosted.org/packages/3c/65/4baa99f1c53b30adf0acd9a5519078871ddde8d2339dc5a7fde80d9d87da/numpy-2.2.6-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:894b3a42502226a1cac872f840030665f33326fc3dac8e57c607905773cdcde3", size = 5084103, upload-time = "2025-05-17T21:35:10.622Z" }, + { url = "https://files.pythonhosted.org/packages/cc/89/e5a34c071a0570cc40c9a54eb472d113eea6d002e9ae12bb3a8407fb912e/numpy-2.2.6-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:71594f7c51a18e728451bb50cc60a3ce4e6538822731b2933209a1f3614e9282", size = 6625382, upload-time = "2025-05-17T21:35:21.414Z" }, + { url = "https://files.pythonhosted.org/packages/f8/35/8c80729f1ff76b3921d5c9487c7ac3de9b2a103b1cd05e905b3090513510/numpy-2.2.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2618db89be1b4e05f7a1a847a9c1c0abd63e63a1607d892dd54668dd92faf87", size = 14018462, upload-time = "2025-05-17T21:35:42.174Z" }, + { url = "https://files.pythonhosted.org/packages/8c/3d/1e1db36cfd41f895d266b103df00ca5b3cbe965184df824dec5c08c6b803/numpy-2.2.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd83c01228a688733f1ded5201c678f0c53ecc1006ffbc404db9f7a899ac6249", size = 16527618, upload-time = "2025-05-17T21:36:06.711Z" }, + { url = "https://files.pythonhosted.org/packages/61/c6/03ed30992602c85aa3cd95b9070a514f8b3c33e31124694438d88809ae36/numpy-2.2.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:37c0ca431f82cd5fa716eca9506aefcabc247fb27ba69c5062a6d3ade8cf8f49", size = 15505511, upload-time = "2025-05-17T21:36:29.965Z" }, + { url = "https://files.pythonhosted.org/packages/b7/25/5761d832a81df431e260719ec45de696414266613c9ee268394dd5ad8236/numpy-2.2.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fe27749d33bb772c80dcd84ae7e8df2adc920ae8297400dabec45f0dedb3f6de", size = 18313783, upload-time = "2025-05-17T21:36:56.883Z" }, + { url = "https://files.pythonhosted.org/packages/57/0a/72d5a3527c5ebffcd47bde9162c39fae1f90138c961e5296491ce778e682/numpy-2.2.6-cp312-cp312-win32.whl", hash = "sha256:4eeaae00d789f66c7a25ac5f34b71a7035bb474e679f410e5e1a94deb24cf2d4", size = 6246506, upload-time = "2025-05-17T21:37:07.368Z" }, + { url = "https://files.pythonhosted.org/packages/36/fa/8c9210162ca1b88529ab76b41ba02d433fd54fecaf6feb70ef9f124683f1/numpy-2.2.6-cp312-cp312-win_amd64.whl", hash = "sha256:c1f9540be57940698ed329904db803cf7a402f3fc200bfe599334c9bd84a40b2", size = 12614190, upload-time = "2025-05-17T21:37:26.213Z" }, + { url = "https://files.pythonhosted.org/packages/f9/5c/6657823f4f594f72b5471f1db1ab12e26e890bb2e41897522d134d2a3e81/numpy-2.2.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:0811bb762109d9708cca4d0b13c4f67146e3c3b7cf8d34018c722adb2d957c84", size = 20867828, upload-time = "2025-05-17T21:37:56.699Z" }, + { url = "https://files.pythonhosted.org/packages/dc/9e/14520dc3dadf3c803473bd07e9b2bd1b69bc583cb2497b47000fed2fa92f/numpy-2.2.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:287cc3162b6f01463ccd86be154f284d0893d2b3ed7292439ea97eafa8170e0b", size = 14143006, upload-time = "2025-05-17T21:38:18.291Z" }, + { url = "https://files.pythonhosted.org/packages/4f/06/7e96c57d90bebdce9918412087fc22ca9851cceaf5567a45c1f404480e9e/numpy-2.2.6-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:f1372f041402e37e5e633e586f62aa53de2eac8d98cbfb822806ce4bbefcb74d", size = 5076765, upload-time = "2025-05-17T21:38:27.319Z" }, + { url = "https://files.pythonhosted.org/packages/73/ed/63d920c23b4289fdac96ddbdd6132e9427790977d5457cd132f18e76eae0/numpy-2.2.6-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:55a4d33fa519660d69614a9fad433be87e5252f4b03850642f88993f7b2ca566", size = 6617736, upload-time = "2025-05-17T21:38:38.141Z" }, + { url = "https://files.pythonhosted.org/packages/85/c5/e19c8f99d83fd377ec8c7e0cf627a8049746da54afc24ef0a0cb73d5dfb5/numpy-2.2.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f92729c95468a2f4f15e9bb94c432a9229d0d50de67304399627a943201baa2f", size = 14010719, upload-time = "2025-05-17T21:38:58.433Z" }, + { url = "https://files.pythonhosted.org/packages/19/49/4df9123aafa7b539317bf6d342cb6d227e49f7a35b99c287a6109b13dd93/numpy-2.2.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1bc23a79bfabc5d056d106f9befb8d50c31ced2fbc70eedb8155aec74a45798f", size = 16526072, upload-time = "2025-05-17T21:39:22.638Z" }, + { url = "https://files.pythonhosted.org/packages/b2/6c/04b5f47f4f32f7c2b0e7260442a8cbcf8168b0e1a41ff1495da42f42a14f/numpy-2.2.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e3143e4451880bed956e706a3220b4e5cf6172ef05fcc397f6f36a550b1dd868", size = 15503213, upload-time = "2025-05-17T21:39:45.865Z" }, + { url = "https://files.pythonhosted.org/packages/17/0a/5cd92e352c1307640d5b6fec1b2ffb06cd0dabe7d7b8227f97933d378422/numpy-2.2.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:b4f13750ce79751586ae2eb824ba7e1e8dba64784086c98cdbbcc6a42112ce0d", size = 18316632, upload-time = "2025-05-17T21:40:13.331Z" }, + { url = "https://files.pythonhosted.org/packages/f0/3b/5cba2b1d88760ef86596ad0f3d484b1cbff7c115ae2429678465057c5155/numpy-2.2.6-cp313-cp313-win32.whl", hash = "sha256:5beb72339d9d4fa36522fc63802f469b13cdbe4fdab4a288f0c441b74272ebfd", size = 6244532, upload-time = "2025-05-17T21:43:46.099Z" }, + { url = "https://files.pythonhosted.org/packages/cb/3b/d58c12eafcb298d4e6d0d40216866ab15f59e55d148a5658bb3132311fcf/numpy-2.2.6-cp313-cp313-win_amd64.whl", hash = "sha256:b0544343a702fa80c95ad5d3d608ea3599dd54d4632df855e4c8d24eb6ecfa1c", size = 12610885, upload-time = "2025-05-17T21:44:05.145Z" }, + { url = "https://files.pythonhosted.org/packages/6b/9e/4bf918b818e516322db999ac25d00c75788ddfd2d2ade4fa66f1f38097e1/numpy-2.2.6-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0bca768cd85ae743b2affdc762d617eddf3bcf8724435498a1e80132d04879e6", size = 20963467, upload-time = "2025-05-17T21:40:44Z" }, + { url = "https://files.pythonhosted.org/packages/61/66/d2de6b291507517ff2e438e13ff7b1e2cdbdb7cb40b3ed475377aece69f9/numpy-2.2.6-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:fc0c5673685c508a142ca65209b4e79ed6740a4ed6b2267dbba90f34b0b3cfda", size = 14225144, upload-time = "2025-05-17T21:41:05.695Z" }, + { url = "https://files.pythonhosted.org/packages/e4/25/480387655407ead912e28ba3a820bc69af9adf13bcbe40b299d454ec011f/numpy-2.2.6-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:5bd4fc3ac8926b3819797a7c0e2631eb889b4118a9898c84f585a54d475b7e40", size = 5200217, upload-time = "2025-05-17T21:41:15.903Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4a/6e313b5108f53dcbf3aca0c0f3e9c92f4c10ce57a0a721851f9785872895/numpy-2.2.6-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:fee4236c876c4e8369388054d02d0e9bb84821feb1a64dd59e137e6511a551f8", size = 6712014, upload-time = "2025-05-17T21:41:27.321Z" }, + { url = "https://files.pythonhosted.org/packages/b7/30/172c2d5c4be71fdf476e9de553443cf8e25feddbe185e0bd88b096915bcc/numpy-2.2.6-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1dda9c7e08dc141e0247a5b8f49cf05984955246a327d4c48bda16821947b2f", size = 14077935, upload-time = "2025-05-17T21:41:49.738Z" }, + { url = "https://files.pythonhosted.org/packages/12/fb/9e743f8d4e4d3c710902cf87af3512082ae3d43b945d5d16563f26ec251d/numpy-2.2.6-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f447e6acb680fd307f40d3da4852208af94afdfab89cf850986c3ca00562f4fa", size = 16600122, upload-time = "2025-05-17T21:42:14.046Z" }, + { url = "https://files.pythonhosted.org/packages/12/75/ee20da0e58d3a66f204f38916757e01e33a9737d0b22373b3eb5a27358f9/numpy-2.2.6-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:389d771b1623ec92636b0786bc4ae56abafad4a4c513d36a55dce14bd9ce8571", size = 15586143, upload-time = "2025-05-17T21:42:37.464Z" }, + { url = "https://files.pythonhosted.org/packages/76/95/bef5b37f29fc5e739947e9ce5179ad402875633308504a52d188302319c8/numpy-2.2.6-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:8e9ace4a37db23421249ed236fdcdd457d671e25146786dfc96835cd951aa7c1", size = 18385260, upload-time = "2025-05-17T21:43:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/09/04/f2f83279d287407cf36a7a8053a5abe7be3622a4363337338f2585e4afda/numpy-2.2.6-cp313-cp313t-win32.whl", hash = "sha256:038613e9fb8c72b0a41f025a7e4c3f0b7a1b5d768ece4796b674c8f3fe13efff", size = 6377225, upload-time = "2025-05-17T21:43:16.254Z" }, + { url = "https://files.pythonhosted.org/packages/67/0e/35082d13c09c02c011cf21570543d202ad929d961c02a147493cb0c2bdf5/numpy-2.2.6-cp313-cp313t-win_amd64.whl", hash = "sha256:6031dd6dfecc0cf9f668681a37648373bddd6421fff6c66ec1624eed0180ee06", size = 12771374, upload-time = "2025-05-17T21:43:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/9e/3b/d94a75f4dbf1ef5d321523ecac21ef23a3cd2ac8b78ae2aac40873590229/numpy-2.2.6-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:0b605b275d7bd0c640cad4e5d30fa701a8d59302e127e5f79138ad62762c3e3d", size = 21040391, upload-time = "2025-05-17T21:44:35.948Z" }, + { url = "https://files.pythonhosted.org/packages/17/f4/09b2fa1b58f0fb4f7c7963a1649c64c4d315752240377ed74d9cd878f7b5/numpy-2.2.6-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:7befc596a7dc9da8a337f79802ee8adb30a552a94f792b9c9d18c840055907db", size = 6786754, upload-time = "2025-05-17T21:44:47.446Z" }, + { url = "https://files.pythonhosted.org/packages/af/30/feba75f143bdc868a1cc3f44ccfa6c4b9ec522b36458e738cd00f67b573f/numpy-2.2.6-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ce47521a4754c8f4593837384bd3424880629f718d87c5d44f8ed763edd63543", size = 16643476, upload-time = "2025-05-17T21:45:11.871Z" }, + { url = "https://files.pythonhosted.org/packages/37/48/ac2a9584402fb6c0cd5b5d1a91dcf176b15760130dd386bbafdbfe3640bf/numpy-2.2.6-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d042d24c90c41b54fd506da306759e06e568864df8ec17ccc17e9e884634fd00", size = 12812666, upload-time = "2025-05-17T21:45:31.426Z" }, +] + +[[package]] +name = "numpy" +version = "2.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", +] +sdist = { url = "https://files.pythonhosted.org/packages/57/fd/0005efbd0af48e55eb3c7208af93f2862d4b1a56cd78e84309a2d959208d/numpy-2.4.2.tar.gz", hash = "sha256:659a6107e31a83c4e33f763942275fd278b21d095094044eb35569e86a21ddae", size = 20723651, upload-time = "2026-01-31T23:13:10.135Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d3/44/71852273146957899753e69986246d6a176061ea183407e95418c2aa4d9a/numpy-2.4.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e7e88598032542bd49af7c4747541422884219056c268823ef6e5e89851c8825", size = 16955478, upload-time = "2026-01-31T23:10:25.623Z" }, + { url = "https://files.pythonhosted.org/packages/74/41/5d17d4058bd0cd96bcbd4d9ff0fb2e21f52702aab9a72e4a594efa18692f/numpy-2.4.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7edc794af8b36ca37ef5fcb5e0d128c7e0595c7b96a2318d1badb6fcd8ee86b1", size = 14965467, upload-time = "2026-01-31T23:10:28.186Z" }, + { url = "https://files.pythonhosted.org/packages/49/48/fb1ce8136c19452ed15f033f8aee91d5defe515094e330ce368a0647846f/numpy-2.4.2-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6e9f61981ace1360e42737e2bae58b27bf28a1b27e781721047d84bd754d32e7", size = 5475172, upload-time = "2026-01-31T23:10:30.848Z" }, + { url = "https://files.pythonhosted.org/packages/40/a9/3feb49f17bbd1300dd2570432961f5c8a4ffeff1db6f02c7273bd020a4c9/numpy-2.4.2-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cb7bbb88aa74908950d979eeaa24dbdf1a865e3c7e45ff0121d8f70387b55f73", size = 6805145, upload-time = "2026-01-31T23:10:32.352Z" }, + { url = "https://files.pythonhosted.org/packages/3f/39/fdf35cbd6d6e2fcad42fcf85ac04a85a0d0fbfbf34b30721c98d602fd70a/numpy-2.4.2-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4f069069931240b3fc703f1e23df63443dbd6390614c8c44a87d96cd0ec81eb1", size = 15966084, upload-time = "2026-01-31T23:10:34.502Z" }, + { url = "https://files.pythonhosted.org/packages/1b/46/6fa4ea94f1ddf969b2ee941290cca6f1bfac92b53c76ae5f44afe17ceb69/numpy-2.4.2-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c02ef4401a506fb60b411467ad501e1429a3487abca4664871d9ae0b46c8ba32", size = 16899477, upload-time = "2026-01-31T23:10:37.075Z" }, + { url = "https://files.pythonhosted.org/packages/09/a1/2a424e162b1a14a5bd860a464ab4e07513916a64ab1683fae262f735ccd2/numpy-2.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2653de5c24910e49c2b106499803124dde62a5a1fe0eedeaecf4309a5f639390", size = 17323429, upload-time = "2026-01-31T23:10:39.704Z" }, + { url = "https://files.pythonhosted.org/packages/ce/a2/73014149ff250628df72c58204822ac01d768697913881aacf839ff78680/numpy-2.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:1ae241bbfc6ae276f94a170b14785e561cb5e7f626b6688cf076af4110887413", size = 18635109, upload-time = "2026-01-31T23:10:41.924Z" }, + { url = "https://files.pythonhosted.org/packages/6c/0c/73e8be2f1accd56df74abc1c5e18527822067dced5ec0861b5bb882c2ce0/numpy-2.4.2-cp311-cp311-win32.whl", hash = "sha256:df1b10187212b198dd45fa943d8985a3c8cf854aed4923796e0e019e113a1bda", size = 6237915, upload-time = "2026-01-31T23:10:45.26Z" }, + { url = "https://files.pythonhosted.org/packages/76/ae/e0265e0163cf127c24c3969d29f1c4c64551a1e375d95a13d32eab25d364/numpy-2.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:b9c618d56a29c9cb1c4da979e9899be7578d2e0b3c24d52079c166324c9e8695", size = 12607972, upload-time = "2026-01-31T23:10:47.021Z" }, + { url = "https://files.pythonhosted.org/packages/29/a5/c43029af9b8014d6ea157f192652c50042e8911f4300f8f6ed3336bf437f/numpy-2.4.2-cp311-cp311-win_arm64.whl", hash = "sha256:47c5a6ed21d9452b10227e5e8a0e1c22979811cad7dcc19d8e3e2fb8fa03f1a3", size = 10485763, upload-time = "2026-01-31T23:10:50.087Z" }, + { url = "https://files.pythonhosted.org/packages/51/6e/6f394c9c77668153e14d4da83bcc247beb5952f6ead7699a1a2992613bea/numpy-2.4.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:21982668592194c609de53ba4933a7471880ccbaadcc52352694a59ecc860b3a", size = 16667963, upload-time = "2026-01-31T23:10:52.147Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f8/55483431f2b2fd015ae6ed4fe62288823ce908437ed49db5a03d15151678/numpy-2.4.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40397bda92382fcec844066efb11f13e1c9a3e2a8e8f318fb72ed8b6db9f60f1", size = 14693571, upload-time = "2026-01-31T23:10:54.789Z" }, + { url = "https://files.pythonhosted.org/packages/2f/20/18026832b1845cdc82248208dd929ca14c9d8f2bac391f67440707fff27c/numpy-2.4.2-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:b3a24467af63c67829bfaa61eecf18d5432d4f11992688537be59ecd6ad32f5e", size = 5203469, upload-time = "2026-01-31T23:10:57.343Z" }, + { url = "https://files.pythonhosted.org/packages/7d/33/2eb97c8a77daaba34eaa3fa7241a14ac5f51c46a6bd5911361b644c4a1e2/numpy-2.4.2-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:805cc8de9fd6e7a22da5aed858e0ab16be5a4db6c873dde1d7451c541553aa27", size = 6550820, upload-time = "2026-01-31T23:10:59.429Z" }, + { url = "https://files.pythonhosted.org/packages/b1/91/b97fdfd12dc75b02c44e26c6638241cc004d4079a0321a69c62f51470c4c/numpy-2.4.2-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6d82351358ffbcdcd7b686b90742a9b86632d6c1c051016484fa0b326a0a1548", size = 15663067, upload-time = "2026-01-31T23:11:01.291Z" }, + { url = "https://files.pythonhosted.org/packages/f5/c6/a18e59f3f0b8071cc85cbc8d80cd02d68aa9710170b2553a117203d46936/numpy-2.4.2-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e35d3e0144137d9fdae62912e869136164534d64a169f86438bc9561b6ad49f", size = 16619782, upload-time = "2026-01-31T23:11:03.669Z" }, + { url = "https://files.pythonhosted.org/packages/b7/83/9751502164601a79e18847309f5ceec0b1446d7b6aa12305759b72cf98b2/numpy-2.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:adb6ed2ad29b9e15321d167d152ee909ec73395901b70936f029c3bc6d7f4460", size = 17013128, upload-time = "2026-01-31T23:11:05.913Z" }, + { url = "https://files.pythonhosted.org/packages/61/c4/c4066322256ec740acc1c8923a10047818691d2f8aec254798f3dd90f5f2/numpy-2.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:8906e71fd8afcb76580404e2a950caef2685df3d2a57fe82a86ac8d33cc007ba", size = 18345324, upload-time = "2026-01-31T23:11:08.248Z" }, + { url = "https://files.pythonhosted.org/packages/ab/af/6157aa6da728fa4525a755bfad486ae7e3f76d4c1864138003eb84328497/numpy-2.4.2-cp312-cp312-win32.whl", hash = "sha256:ec055f6dae239a6299cace477b479cca2fc125c5675482daf1dd886933a1076f", size = 5960282, upload-time = "2026-01-31T23:11:10.497Z" }, + { url = "https://files.pythonhosted.org/packages/92/0f/7ceaaeaacb40567071e94dbf2c9480c0ae453d5bb4f52bea3892c39dc83c/numpy-2.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:209fae046e62d0ce6435fcfe3b1a10537e858249b3d9b05829e2a05218296a85", size = 12314210, upload-time = "2026-01-31T23:11:12.176Z" }, + { url = "https://files.pythonhosted.org/packages/2f/a3/56c5c604fae6dd40fa2ed3040d005fca97e91bd320d232ac9931d77ba13c/numpy-2.4.2-cp312-cp312-win_arm64.whl", hash = "sha256:fbde1b0c6e81d56f5dccd95dd4a711d9b95df1ae4009a60887e56b27e8d903fa", size = 10220171, upload-time = "2026-01-31T23:11:14.684Z" }, + { url = "https://files.pythonhosted.org/packages/a1/22/815b9fe25d1d7ae7d492152adbc7226d3eff731dffc38fe970589fcaaa38/numpy-2.4.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:25f2059807faea4b077a2b6837391b5d830864b3543627f381821c646f31a63c", size = 16663696, upload-time = "2026-01-31T23:11:17.516Z" }, + { url = "https://files.pythonhosted.org/packages/09/f0/817d03a03f93ba9c6c8993de509277d84e69f9453601915e4a69554102a1/numpy-2.4.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bd3a7a9f5847d2fb8c2c6d1c862fa109c31a9abeca1a3c2bd5a64572955b2979", size = 14688322, upload-time = "2026-01-31T23:11:19.883Z" }, + { url = "https://files.pythonhosted.org/packages/da/b4/f805ab79293c728b9a99438775ce51885fd4f31b76178767cfc718701a39/numpy-2.4.2-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:8e4549f8a3c6d13d55041925e912bfd834285ef1dd64d6bc7d542583355e2e98", size = 5198157, upload-time = "2026-01-31T23:11:22.375Z" }, + { url = "https://files.pythonhosted.org/packages/74/09/826e4289844eccdcd64aac27d13b0fd3f32039915dd5b9ba01baae1f436c/numpy-2.4.2-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:aea4f66ff44dfddf8c2cffd66ba6538c5ec67d389285292fe428cb2c738c8aef", size = 6546330, upload-time = "2026-01-31T23:11:23.958Z" }, + { url = "https://files.pythonhosted.org/packages/19/fb/cbfdbfa3057a10aea5422c558ac57538e6acc87ec1669e666d32ac198da7/numpy-2.4.2-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c3cd545784805de05aafe1dde61752ea49a359ccba9760c1e5d1c88a93bbf2b7", size = 15660968, upload-time = "2026-01-31T23:11:25.713Z" }, + { url = "https://files.pythonhosted.org/packages/04/dc/46066ce18d01645541f0186877377b9371b8fa8017fa8262002b4ef22612/numpy-2.4.2-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d0d9b7c93578baafcbc5f0b83eaf17b79d345c6f36917ba0c67f45226911d499", size = 16607311, upload-time = "2026-01-31T23:11:28.117Z" }, + { url = "https://files.pythonhosted.org/packages/14/d9/4b5adfc39a43fa6bf918c6d544bc60c05236cc2f6339847fc5b35e6cb5b0/numpy-2.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:f74f0f7779cc7ae07d1810aab8ac6b1464c3eafb9e283a40da7309d5e6e48fbb", size = 17012850, upload-time = "2026-01-31T23:11:30.888Z" }, + { url = "https://files.pythonhosted.org/packages/b7/20/adb6e6adde6d0130046e6fdfb7675cc62bc2f6b7b02239a09eb58435753d/numpy-2.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:c7ac672d699bf36275c035e16b65539931347d68b70667d28984c9fb34e07fa7", size = 18334210, upload-time = "2026-01-31T23:11:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/78/0e/0a73b3dff26803a8c02baa76398015ea2a5434d9b8265a7898a6028c1591/numpy-2.4.2-cp313-cp313-win32.whl", hash = "sha256:8e9afaeb0beff068b4d9cd20d322ba0ee1cecfb0b08db145e4ab4dd44a6b5110", size = 5958199, upload-time = "2026-01-31T23:11:35.385Z" }, + { url = "https://files.pythonhosted.org/packages/43/bc/6352f343522fcb2c04dbaf94cb30cca6fd32c1a750c06ad6231b4293708c/numpy-2.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:7df2de1e4fba69a51c06c28f5a3de36731eb9639feb8e1cf7e4a7b0daf4cf622", size = 12310848, upload-time = "2026-01-31T23:11:38.001Z" }, + { url = "https://files.pythonhosted.org/packages/6e/8d/6da186483e308da5da1cc6918ce913dcfe14ffde98e710bfeff2a6158d4e/numpy-2.4.2-cp313-cp313-win_arm64.whl", hash = "sha256:0fece1d1f0a89c16b03442eae5c56dc0be0c7883b5d388e0c03f53019a4bfd71", size = 10221082, upload-time = "2026-01-31T23:11:40.392Z" }, + { url = "https://files.pythonhosted.org/packages/25/a1/9510aa43555b44781968935c7548a8926274f815de42ad3997e9e83680dd/numpy-2.4.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5633c0da313330fd20c484c78cdd3f9b175b55e1a766c4a174230c6b70ad8262", size = 14815866, upload-time = "2026-01-31T23:11:42.495Z" }, + { url = "https://files.pythonhosted.org/packages/36/30/6bbb5e76631a5ae46e7923dd16ca9d3f1c93cfa8d4ed79a129814a9d8db3/numpy-2.4.2-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:d9f64d786b3b1dd742c946c42d15b07497ed14af1a1f3ce840cce27daa0ce913", size = 5325631, upload-time = "2026-01-31T23:11:44.7Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/3a490938800c1923b567b3a15cd17896e68052e2145d8662aaf3e1ffc58f/numpy-2.4.2-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:b21041e8cb6a1eb5312dd1d2f80a94d91efffb7a06b70597d44f1bd2dfc315ab", size = 6646254, upload-time = "2026-01-31T23:11:46.341Z" }, + { url = "https://files.pythonhosted.org/packages/d3/e9/fac0890149898a9b609caa5af7455a948b544746e4b8fe7c212c8edd71f8/numpy-2.4.2-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00ab83c56211a1d7c07c25e3217ea6695e50a3e2f255053686b081dc0b091a82", size = 15720138, upload-time = "2026-01-31T23:11:48.082Z" }, + { url = "https://files.pythonhosted.org/packages/ea/5c/08887c54e68e1e28df53709f1893ce92932cc6f01f7c3d4dc952f61ffd4e/numpy-2.4.2-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2fb882da679409066b4603579619341c6d6898fc83a8995199d5249f986e8e8f", size = 16655398, upload-time = "2026-01-31T23:11:50.293Z" }, + { url = "https://files.pythonhosted.org/packages/4d/89/253db0fa0e66e9129c745e4ef25631dc37d5f1314dad2b53e907b8538e6d/numpy-2.4.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:66cb9422236317f9d44b67b4d18f44efe6e9c7f8794ac0462978513359461554", size = 17079064, upload-time = "2026-01-31T23:11:52.927Z" }, + { url = "https://files.pythonhosted.org/packages/2a/d5/cbade46ce97c59c6c3da525e8d95b7abe8a42974a1dc5c1d489c10433e88/numpy-2.4.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:0f01dcf33e73d80bd8dc0f20a71303abbafa26a19e23f6b68d1aa9990af90257", size = 18379680, upload-time = "2026-01-31T23:11:55.22Z" }, + { url = "https://files.pythonhosted.org/packages/40/62/48f99ae172a4b63d981babe683685030e8a3df4f246c893ea5c6ef99f018/numpy-2.4.2-cp313-cp313t-win32.whl", hash = "sha256:52b913ec40ff7ae845687b0b34d8d93b60cb66dcee06996dd5c99f2fc9328657", size = 6082433, upload-time = "2026-01-31T23:11:58.096Z" }, + { url = "https://files.pythonhosted.org/packages/07/38/e054a61cfe48ad9f1ed0d188e78b7e26859d0b60ef21cd9de4897cdb5326/numpy-2.4.2-cp313-cp313t-win_amd64.whl", hash = "sha256:5eea80d908b2c1f91486eb95b3fb6fab187e569ec9752ab7d9333d2e66bf2d6b", size = 12451181, upload-time = "2026-01-31T23:11:59.782Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a4/a05c3a6418575e185dd84d0b9680b6bb2e2dc3e4202f036b7b4e22d6e9dc/numpy-2.4.2-cp313-cp313t-win_arm64.whl", hash = "sha256:fd49860271d52127d61197bb50b64f58454e9f578cb4b2c001a6de8b1f50b0b1", size = 10290756, upload-time = "2026-01-31T23:12:02.438Z" }, + { url = "https://files.pythonhosted.org/packages/18/88/b7df6050bf18fdcfb7046286c6535cabbdd2064a3440fca3f069d319c16e/numpy-2.4.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:444be170853f1f9d528428eceb55f12918e4fda5d8805480f36a002f1415e09b", size = 16663092, upload-time = "2026-01-31T23:12:04.521Z" }, + { url = "https://files.pythonhosted.org/packages/25/7a/1fee4329abc705a469a4afe6e69b1ef7e915117747886327104a8493a955/numpy-2.4.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:d1240d50adff70c2a88217698ca844723068533f3f5c5fa6ee2e3220e3bdb000", size = 14698770, upload-time = "2026-01-31T23:12:06.96Z" }, + { url = "https://files.pythonhosted.org/packages/fb/0b/f9e49ba6c923678ad5bc38181c08ac5e53b7a5754dbca8e581aa1a56b1ff/numpy-2.4.2-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:7cdde6de52fb6664b00b056341265441192d1291c130e99183ec0d4b110ff8b1", size = 5208562, upload-time = "2026-01-31T23:12:09.632Z" }, + { url = "https://files.pythonhosted.org/packages/7d/12/d7de8f6f53f9bb76997e5e4c069eda2051e3fe134e9181671c4391677bb2/numpy-2.4.2-cp314-cp314-macosx_14_0_x86_64.whl", hash = "sha256:cda077c2e5b780200b6b3e09d0b42205a3d1c68f30c6dceb90401c13bff8fe74", size = 6543710, upload-time = "2026-01-31T23:12:11.969Z" }, + { url = "https://files.pythonhosted.org/packages/09/63/c66418c2e0268a31a4cf8a8b512685748200f8e8e8ec6c507ce14e773529/numpy-2.4.2-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d30291931c915b2ab5717c2974bb95ee891a1cf22ebc16a8006bd59cd210d40a", size = 15677205, upload-time = "2026-01-31T23:12:14.33Z" }, + { url = "https://files.pythonhosted.org/packages/5d/6c/7f237821c9642fb2a04d2f1e88b4295677144ca93285fd76eff3bcba858d/numpy-2.4.2-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bba37bc29d4d85761deed3954a1bc62be7cf462b9510b51d367b769a8c8df325", size = 16611738, upload-time = "2026-01-31T23:12:16.525Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a7/39c4cdda9f019b609b5c473899d87abff092fc908cfe4d1ecb2fcff453b0/numpy-2.4.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b2f0073ed0868db1dcd86e052d37279eef185b9c8db5bf61f30f46adac63c909", size = 17028888, upload-time = "2026-01-31T23:12:19.306Z" }, + { url = "https://files.pythonhosted.org/packages/da/b3/e84bb64bdfea967cc10950d71090ec2d84b49bc691df0025dddb7c26e8e3/numpy-2.4.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7f54844851cdb630ceb623dcec4db3240d1ac13d4990532446761baede94996a", size = 18339556, upload-time = "2026-01-31T23:12:21.816Z" }, + { url = "https://files.pythonhosted.org/packages/88/f5/954a291bc1192a27081706862ac62bb5920fbecfbaa302f64682aa90beed/numpy-2.4.2-cp314-cp314-win32.whl", hash = "sha256:12e26134a0331d8dbd9351620f037ec470b7c75929cb8a1537f6bfe411152a1a", size = 6006899, upload-time = "2026-01-31T23:12:24.14Z" }, + { url = "https://files.pythonhosted.org/packages/05/cb/eff72a91b2efdd1bc98b3b8759f6a1654aa87612fc86e3d87d6fe4f948c4/numpy-2.4.2-cp314-cp314-win_amd64.whl", hash = "sha256:068cdb2d0d644cdb45670810894f6a0600797a69c05f1ac478e8d31670b8ee75", size = 12443072, upload-time = "2026-01-31T23:12:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/37/75/62726948db36a56428fce4ba80a115716dc4fad6a3a4352487f8bb950966/numpy-2.4.2-cp314-cp314-win_arm64.whl", hash = "sha256:6ed0be1ee58eef41231a5c943d7d1375f093142702d5723ca2eb07db9b934b05", size = 10494886, upload-time = "2026-01-31T23:12:28.488Z" }, + { url = "https://files.pythonhosted.org/packages/36/2f/ee93744f1e0661dc267e4b21940870cabfae187c092e1433b77b09b50ac4/numpy-2.4.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:98f16a80e917003a12c0580f97b5f875853ebc33e2eaa4bccfc8201ac6869308", size = 14818567, upload-time = "2026-01-31T23:12:30.709Z" }, + { url = "https://files.pythonhosted.org/packages/a7/24/6535212add7d76ff938d8bdc654f53f88d35cddedf807a599e180dcb8e66/numpy-2.4.2-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:20abd069b9cda45874498b245c8015b18ace6de8546bf50dfa8cea1696ed06ef", size = 5328372, upload-time = "2026-01-31T23:12:32.962Z" }, + { url = "https://files.pythonhosted.org/packages/5e/9d/c48f0a035725f925634bf6b8994253b43f2047f6778a54147d7e213bc5a7/numpy-2.4.2-cp314-cp314t-macosx_14_0_x86_64.whl", hash = "sha256:e98c97502435b53741540a5717a6749ac2ada901056c7db951d33e11c885cc7d", size = 6649306, upload-time = "2026-01-31T23:12:34.797Z" }, + { url = "https://files.pythonhosted.org/packages/81/05/7c73a9574cd4a53a25907bad38b59ac83919c0ddc8234ec157f344d57d9a/numpy-2.4.2-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:da6cad4e82cb893db4b69105c604d805e0c3ce11501a55b5e9f9083b47d2ffe8", size = 15722394, upload-time = "2026-01-31T23:12:36.565Z" }, + { url = "https://files.pythonhosted.org/packages/35/fa/4de10089f21fc7d18442c4a767ab156b25c2a6eaf187c0db6d9ecdaeb43f/numpy-2.4.2-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e4424677ce4b47fe73c8b5556d876571f7c6945d264201180db2dc34f676ab5", size = 16653343, upload-time = "2026-01-31T23:12:39.188Z" }, + { url = "https://files.pythonhosted.org/packages/b8/f9/d33e4ffc857f3763a57aa85650f2e82486832d7492280ac21ba9efda80da/numpy-2.4.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2b8f157c8a6f20eb657e240f8985cc135598b2b46985c5bccbde7616dc9c6b1e", size = 17078045, upload-time = "2026-01-31T23:12:42.041Z" }, + { url = "https://files.pythonhosted.org/packages/c8/b8/54bdb43b6225badbea6389fa038c4ef868c44f5890f95dd530a218706da3/numpy-2.4.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:5daf6f3914a733336dab21a05cdec343144600e964d2fcdabaac0c0269874b2a", size = 18380024, upload-time = "2026-01-31T23:12:44.331Z" }, + { url = "https://files.pythonhosted.org/packages/a5/55/6e1a61ded7af8df04016d81b5b02daa59f2ea9252ee0397cb9f631efe9e5/numpy-2.4.2-cp314-cp314t-win32.whl", hash = "sha256:8c50dd1fc8826f5b26a5ee4d77ca55d88a895f4e4819c7ecc2a9f5905047a443", size = 6153937, upload-time = "2026-01-31T23:12:47.229Z" }, + { url = "https://files.pythonhosted.org/packages/45/aa/fa6118d1ed6d776b0983f3ceac9b1a5558e80df9365b1c3aa6d42bf9eee4/numpy-2.4.2-cp314-cp314t-win_amd64.whl", hash = "sha256:fcf92bee92742edd401ba41135185866f7026c502617f422eb432cfeca4fe236", size = 12631844, upload-time = "2026-01-31T23:12:48.997Z" }, + { url = "https://files.pythonhosted.org/packages/32/0a/2ec5deea6dcd158f254a7b372fb09cfba5719419c8d66343bab35237b3fb/numpy-2.4.2-cp314-cp314t-win_arm64.whl", hash = "sha256:1f92f53998a17265194018d1cc321b2e96e900ca52d54c7c77837b71b9465181", size = 10565379, upload-time = "2026-01-31T23:12:51.345Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f8/50e14d36d915ef64d8f8bc4a087fc8264d82c785eda6711f80ab7e620335/numpy-2.4.2-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:89f7268c009bc492f506abd6f5265defa7cb3f7487dc21d357c3d290add45082", size = 16833179, upload-time = "2026-01-31T23:12:53.5Z" }, + { url = "https://files.pythonhosted.org/packages/17/17/809b5cad63812058a8189e91a1e2d55a5a18fd04611dbad244e8aeae465c/numpy-2.4.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6dee3bb76aa4009d5a912180bf5b2de012532998d094acee25d9cb8dee3e44a", size = 14889755, upload-time = "2026-01-31T23:12:55.933Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ea/181b9bcf7627fc8371720316c24db888dcb9829b1c0270abf3d288b2e29b/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_arm64.whl", hash = "sha256:cd2bd2bbed13e213d6b55dc1d035a4f91748a7d3edc9480c13898b0353708920", size = 5399500, upload-time = "2026-01-31T23:12:58.671Z" }, + { url = "https://files.pythonhosted.org/packages/33/9f/413adf3fc955541ff5536b78fcf0754680b3c6d95103230252a2c9408d23/numpy-2.4.2-pp311-pypy311_pp73-macosx_14_0_x86_64.whl", hash = "sha256:cf28c0c1d4c4bf00f509fa7eb02c58d7caf221b50b467bcb0d9bbf1584d5c821", size = 6714252, upload-time = "2026-01-31T23:13:00.518Z" }, + { url = "https://files.pythonhosted.org/packages/91/da/643aad274e29ccbdf42ecd94dafe524b81c87bcb56b83872d54827f10543/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e04ae107ac591763a47398bb45b568fc38f02dbc4aa44c063f67a131f99346cb", size = 15797142, upload-time = "2026-01-31T23:13:02.219Z" }, + { url = "https://files.pythonhosted.org/packages/66/27/965b8525e9cb5dc16481b30a1b3c21e50c7ebf6e9dbd48d0c4d0d5089c7e/numpy-2.4.2-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:602f65afdef699cda27ec0b9224ae5dc43e328f4c24c689deaf77133dbee74d0", size = 16727979, upload-time = "2026-01-31T23:13:04.62Z" }, + { url = "https://files.pythonhosted.org/packages/de/e5/b7d20451657664b07986c2f6e3be564433f5dcaf3482d68eaecd79afaf03/numpy-2.4.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be71bf1edb48ebbbf7f6337b5bfd2f895d1902f6335a5830b20141fc126ffba0", size = 12502577, upload-time = "2026-01-31T23:13:07.08Z" }, +] + +[[package]] +name = "packaging" +version = "26.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/65/ee/299d360cdc32edc7d2cf530f3accf79c4fca01e96ffc950d8a52213bd8e4/packaging-26.0.tar.gz", hash = "sha256:00243ae351a257117b6a241061796684b084ed1c516a08c48a3f7e147a9d80b4", size = 143416, upload-time = "2026-01-21T20:50:39.064Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, +] + +[[package]] +name = "parso" +version = "0.8.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/81/76/a1e769043c0c0c9fe391b702539d594731a4362334cdf4dc25d0c09761e7/parso-0.8.6.tar.gz", hash = "sha256:2b9a0332696df97d454fa67b81618fd69c35a7b90327cbe6ba5c92d2c68a7bfd", size = 401621, upload-time = "2026-02-09T15:45:24.425Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b6/61/fae042894f4296ec49e3f193aff5d7c18440da9e48102c3315e1bc4519a7/parso-0.8.6-py2.py3-none-any.whl", hash = "sha256:2c549f800b70a5c4952197248825584cb00f033b29c692671d3bf08bf380baff", size = 106894, upload-time = "2026-02-09T15:45:21.391Z" }, +] + +[[package]] +name = "patchelf" +version = "0.17.2.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/a3/fdd3fa938c864aa2f11dd0b7f08befeda983d2dcdee44da493c6977a653f/patchelf-0.17.2.4.tar.gz", hash = "sha256:970ee5cd8af33e5ea2099510b2f9013fa1b8d5cd763bf3fd3961281c18101a09", size = 149629, upload-time = "2025-07-23T21:16:32.071Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/a7/8c4f86c78ec03db954d05fd9c57a114cc3a172a2d3e4a8b949cd5ff89471/patchelf-0.17.2.4-py3-none-macosx_10_9_universal2.whl", hash = "sha256:343bb1b94e959f9070ca9607453b04390e36bbaa33c88640b989cefad0aa049e", size = 184436, upload-time = "2025-07-23T21:16:20.578Z" }, + { url = "https://files.pythonhosted.org/packages/7e/19/f7821ef31aab01fa7dc8ebe697ece88ec4f7a0fdd3155dab2dfee4b00e5c/patchelf-0.17.2.4-py3-none-manylinux1_x86_64.manylinux_2_5_x86_64.musllinux_1_1_x86_64.whl", hash = "sha256:d9b35ebfada70c02679ad036407d9724ffe1255122ba4ac5e4be5868618a5689", size = 482846, upload-time = "2025-07-23T21:16:23.73Z" }, + { url = "https://files.pythonhosted.org/packages/d1/50/107fea848ecfd851d473b079cab79107487d72c4c3cdb25b9d2603a24ca2/patchelf-0.17.2.4-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:2931a1b5b85f3549661898af7bf746afbda7903c7c9a967cfc998a3563f84fad", size = 477811, upload-time = "2025-07-23T21:16:25.145Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + +[[package]] +name = "pexpect" +version = "4.9.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "ptyprocess" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/92/cc564bf6381ff43ce1f4d06852fc19a2f11d180f23dc32d9588bee2f149d/pexpect-4.9.0.tar.gz", hash = "sha256:ee7d41123f3c9911050ea2c2dac107568dc43b2d3b0c7557a33212c398ead30f", size = 166450, upload-time = "2023-11-25T09:07:26.339Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9e/c3/059298687310d527a58bb01f3b1965787ee3b40dce76752eda8b44e9a2c5/pexpect-4.9.0-py2.py3-none-any.whl", hash = "sha256:7236d1e080e4936be2dc3e326cec0af72acf9212a7e1d060210e70a47e253523", size = 63772, upload-time = "2023-11-25T06:56:14.81Z" }, +] + +[[package]] +name = "pillow" +version = "11.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/0d/d0d6dea55cd152ce3d6767bb38a8fc10e33796ba4ba210cbab9354b6d238/pillow-11.3.0.tar.gz", hash = "sha256:3828ee7586cd0b2091b6209e5ad53e20d0649bbe87164a459d0676e035e8f523", size = 47113069, upload-time = "2025-07-01T09:16:30.666Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4c/5d/45a3553a253ac8763f3561371432a90bdbe6000fbdcf1397ffe502aa206c/pillow-11.3.0-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1b9c17fd4ace828b3003dfd1e30bff24863e0eb59b535e8f80194d9cc7ecf860", size = 5316554, upload-time = "2025-07-01T09:13:39.342Z" }, + { url = "https://files.pythonhosted.org/packages/7c/c8/67c12ab069ef586a25a4a79ced553586748fad100c77c0ce59bb4983ac98/pillow-11.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:65dc69160114cdd0ca0f35cb434633c75e8e7fad4cf855177a05bf38678f73ad", size = 4686548, upload-time = "2025-07-01T09:13:41.835Z" }, + { url = "https://files.pythonhosted.org/packages/2f/bd/6741ebd56263390b382ae4c5de02979af7f8bd9807346d068700dd6d5cf9/pillow-11.3.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7107195ddc914f656c7fc8e4a5e1c25f32e9236ea3ea860f257b0436011fddd0", size = 5859742, upload-time = "2025-07-03T13:09:47.439Z" }, + { url = "https://files.pythonhosted.org/packages/ca/0b/c412a9e27e1e6a829e6ab6c2dca52dd563efbedf4c9c6aa453d9a9b77359/pillow-11.3.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cc3e831b563b3114baac7ec2ee86819eb03caa1a2cef0b481a5675b59c4fe23b", size = 7633087, upload-time = "2025-07-03T13:09:51.796Z" }, + { url = "https://files.pythonhosted.org/packages/59/9d/9b7076aaf30f5dd17e5e5589b2d2f5a5d7e30ff67a171eb686e4eecc2adf/pillow-11.3.0-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1f182ebd2303acf8c380a54f615ec883322593320a9b00438eb842c1f37ae50", size = 5963350, upload-time = "2025-07-01T09:13:43.865Z" }, + { url = "https://files.pythonhosted.org/packages/f0/16/1a6bf01fb622fb9cf5c91683823f073f053005c849b1f52ed613afcf8dae/pillow-11.3.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4445fa62e15936a028672fd48c4c11a66d641d2c05726c7ec1f8ba6a572036ae", size = 6631840, upload-time = "2025-07-01T09:13:46.161Z" }, + { url = "https://files.pythonhosted.org/packages/7b/e6/6ff7077077eb47fde78739e7d570bdcd7c10495666b6afcd23ab56b19a43/pillow-11.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:71f511f6b3b91dd543282477be45a033e4845a40278fa8dcdbfdb07109bf18f9", size = 6074005, upload-time = "2025-07-01T09:13:47.829Z" }, + { url = "https://files.pythonhosted.org/packages/c3/3a/b13f36832ea6d279a697231658199e0a03cd87ef12048016bdcc84131601/pillow-11.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:040a5b691b0713e1f6cbe222e0f4f74cd233421e105850ae3b3c0ceda520f42e", size = 6708372, upload-time = "2025-07-01T09:13:52.145Z" }, + { url = "https://files.pythonhosted.org/packages/6c/e4/61b2e1a7528740efbc70b3d581f33937e38e98ef3d50b05007267a55bcb2/pillow-11.3.0-cp310-cp310-win32.whl", hash = "sha256:89bd777bc6624fe4115e9fac3352c79ed60f3bb18651420635f26e643e3dd1f6", size = 6277090, upload-time = "2025-07-01T09:13:53.915Z" }, + { url = "https://files.pythonhosted.org/packages/a9/d3/60c781c83a785d6afbd6a326ed4d759d141de43aa7365725cbcd65ce5e54/pillow-11.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:19d2ff547c75b8e3ff46f4d9ef969a06c30ab2d4263a9e287733aa8b2429ce8f", size = 6985988, upload-time = "2025-07-01T09:13:55.699Z" }, + { url = "https://files.pythonhosted.org/packages/9f/28/4f4a0203165eefb3763939c6789ba31013a2e90adffb456610f30f613850/pillow-11.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:819931d25e57b513242859ce1876c58c59dc31587847bf74cfe06b2e0cb22d2f", size = 2422899, upload-time = "2025-07-01T09:13:57.497Z" }, + { url = "https://files.pythonhosted.org/packages/db/26/77f8ed17ca4ffd60e1dcd220a6ec6d71210ba398cfa33a13a1cd614c5613/pillow-11.3.0-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:1cd110edf822773368b396281a2293aeb91c90a2db00d78ea43e7e861631b722", size = 5316531, upload-time = "2025-07-01T09:13:59.203Z" }, + { url = "https://files.pythonhosted.org/packages/cb/39/ee475903197ce709322a17a866892efb560f57900d9af2e55f86db51b0a5/pillow-11.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9c412fddd1b77a75aa904615ebaa6001f169b26fd467b4be93aded278266b288", size = 4686560, upload-time = "2025-07-01T09:14:01.101Z" }, + { url = "https://files.pythonhosted.org/packages/d5/90/442068a160fd179938ba55ec8c97050a612426fae5ec0a764e345839f76d/pillow-11.3.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1aa4de119a0ecac0a34a9c8bde33f34022e2e8f99104e47a3ca392fd60e37d", size = 5870978, upload-time = "2025-07-03T13:09:55.638Z" }, + { url = "https://files.pythonhosted.org/packages/13/92/dcdd147ab02daf405387f0218dcf792dc6dd5b14d2573d40b4caeef01059/pillow-11.3.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:91da1d88226663594e3f6b4b8c3c8d85bd504117d043740a8e0ec449087cc494", size = 7641168, upload-time = "2025-07-03T13:10:00.37Z" }, + { url = "https://files.pythonhosted.org/packages/6e/db/839d6ba7fd38b51af641aa904e2960e7a5644d60ec754c046b7d2aee00e5/pillow-11.3.0-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:643f189248837533073c405ec2f0bb250ba54598cf80e8c1e043381a60632f58", size = 5973053, upload-time = "2025-07-01T09:14:04.491Z" }, + { url = "https://files.pythonhosted.org/packages/f2/2f/d7675ecae6c43e9f12aa8d58b6012683b20b6edfbdac7abcb4e6af7a3784/pillow-11.3.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:106064daa23a745510dabce1d84f29137a37224831d88eb4ce94bb187b1d7e5f", size = 6640273, upload-time = "2025-07-01T09:14:06.235Z" }, + { url = "https://files.pythonhosted.org/packages/45/ad/931694675ede172e15b2ff03c8144a0ddaea1d87adb72bb07655eaffb654/pillow-11.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd8ff254faf15591e724dc7c4ddb6bf4793efcbe13802a4ae3e863cd300b493e", size = 6082043, upload-time = "2025-07-01T09:14:07.978Z" }, + { url = "https://files.pythonhosted.org/packages/3a/04/ba8f2b11fc80d2dd462d7abec16351b45ec99cbbaea4387648a44190351a/pillow-11.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:932c754c2d51ad2b2271fd01c3d121daaa35e27efae2a616f77bf164bc0b3e94", size = 6715516, upload-time = "2025-07-01T09:14:10.233Z" }, + { url = "https://files.pythonhosted.org/packages/48/59/8cd06d7f3944cc7d892e8533c56b0acb68399f640786313275faec1e3b6f/pillow-11.3.0-cp311-cp311-win32.whl", hash = "sha256:b4b8f3efc8d530a1544e5962bd6b403d5f7fe8b9e08227c6b255f98ad82b4ba0", size = 6274768, upload-time = "2025-07-01T09:14:11.921Z" }, + { url = "https://files.pythonhosted.org/packages/f1/cc/29c0f5d64ab8eae20f3232da8f8571660aa0ab4b8f1331da5c2f5f9a938e/pillow-11.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:1a992e86b0dd7aeb1f053cd506508c0999d710a8f07b4c791c63843fc6a807ac", size = 6986055, upload-time = "2025-07-01T09:14:13.623Z" }, + { url = "https://files.pythonhosted.org/packages/c6/df/90bd886fabd544c25addd63e5ca6932c86f2b701d5da6c7839387a076b4a/pillow-11.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:30807c931ff7c095620fe04448e2c2fc673fcbb1ffe2a7da3fb39613489b1ddd", size = 2423079, upload-time = "2025-07-01T09:14:15.268Z" }, + { url = "https://files.pythonhosted.org/packages/40/fe/1bc9b3ee13f68487a99ac9529968035cca2f0a51ec36892060edcc51d06a/pillow-11.3.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:fdae223722da47b024b867c1ea0be64e0df702c5e0a60e27daad39bf960dd1e4", size = 5278800, upload-time = "2025-07-01T09:14:17.648Z" }, + { url = "https://files.pythonhosted.org/packages/2c/32/7e2ac19b5713657384cec55f89065fb306b06af008cfd87e572035b27119/pillow-11.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:921bd305b10e82b4d1f5e802b6850677f965d8394203d182f078873851dada69", size = 4686296, upload-time = "2025-07-01T09:14:19.828Z" }, + { url = "https://files.pythonhosted.org/packages/8e/1e/b9e12bbe6e4c2220effebc09ea0923a07a6da1e1f1bfbc8d7d29a01ce32b/pillow-11.3.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:eb76541cba2f958032d79d143b98a3a6b3ea87f0959bbe256c0b5e416599fd5d", size = 5871726, upload-time = "2025-07-03T13:10:04.448Z" }, + { url = "https://files.pythonhosted.org/packages/8d/33/e9200d2bd7ba00dc3ddb78df1198a6e80d7669cce6c2bdbeb2530a74ec58/pillow-11.3.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:67172f2944ebba3d4a7b54f2e95c786a3a50c21b88456329314caaa28cda70f6", size = 7644652, upload-time = "2025-07-03T13:10:10.391Z" }, + { url = "https://files.pythonhosted.org/packages/41/f1/6f2427a26fc683e00d985bc391bdd76d8dd4e92fac33d841127eb8fb2313/pillow-11.3.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97f07ed9f56a3b9b5f49d3661dc9607484e85c67e27f3e8be2c7d28ca032fec7", size = 5977787, upload-time = "2025-07-01T09:14:21.63Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c9/06dd4a38974e24f932ff5f98ea3c546ce3f8c995d3f0985f8e5ba48bba19/pillow-11.3.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:676b2815362456b5b3216b4fd5bd89d362100dc6f4945154ff172e206a22c024", size = 6645236, upload-time = "2025-07-01T09:14:23.321Z" }, + { url = "https://files.pythonhosted.org/packages/40/e7/848f69fb79843b3d91241bad658e9c14f39a32f71a301bcd1d139416d1be/pillow-11.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3e184b2f26ff146363dd07bde8b711833d7b0202e27d13540bfe2e35a323a809", size = 6086950, upload-time = "2025-07-01T09:14:25.237Z" }, + { url = "https://files.pythonhosted.org/packages/0b/1a/7cff92e695a2a29ac1958c2a0fe4c0b2393b60aac13b04a4fe2735cad52d/pillow-11.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6be31e3fc9a621e071bc17bb7de63b85cbe0bfae91bb0363c893cbe67247780d", size = 6723358, upload-time = "2025-07-01T09:14:27.053Z" }, + { url = "https://files.pythonhosted.org/packages/26/7d/73699ad77895f69edff76b0f332acc3d497f22f5d75e5360f78cbcaff248/pillow-11.3.0-cp312-cp312-win32.whl", hash = "sha256:7b161756381f0918e05e7cb8a371fff367e807770f8fe92ecb20d905d0e1c149", size = 6275079, upload-time = "2025-07-01T09:14:30.104Z" }, + { url = "https://files.pythonhosted.org/packages/8c/ce/e7dfc873bdd9828f3b6e5c2bbb74e47a98ec23cc5c74fc4e54462f0d9204/pillow-11.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:a6444696fce635783440b7f7a9fc24b3ad10a9ea3f0ab66c5905be1c19ccf17d", size = 6986324, upload-time = "2025-07-01T09:14:31.899Z" }, + { url = "https://files.pythonhosted.org/packages/16/8f/b13447d1bf0b1f7467ce7d86f6e6edf66c0ad7cf44cf5c87a37f9bed9936/pillow-11.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:2aceea54f957dd4448264f9bf40875da0415c83eb85f55069d89c0ed436e3542", size = 2423067, upload-time = "2025-07-01T09:14:33.709Z" }, + { url = "https://files.pythonhosted.org/packages/1e/93/0952f2ed8db3a5a4c7a11f91965d6184ebc8cd7cbb7941a260d5f018cd2d/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:1c627742b539bba4309df89171356fcb3cc5a9178355b2727d1b74a6cf155fbd", size = 2128328, upload-time = "2025-07-01T09:14:35.276Z" }, + { url = "https://files.pythonhosted.org/packages/4b/e8/100c3d114b1a0bf4042f27e0f87d2f25e857e838034e98ca98fe7b8c0a9c/pillow-11.3.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:30b7c02f3899d10f13d7a48163c8969e4e653f8b43416d23d13d1bbfdc93b9f8", size = 2170652, upload-time = "2025-07-01T09:14:37.203Z" }, + { url = "https://files.pythonhosted.org/packages/aa/86/3f758a28a6e381758545f7cdb4942e1cb79abd271bea932998fc0db93cb6/pillow-11.3.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:7859a4cc7c9295f5838015d8cc0a9c215b77e43d07a25e460f35cf516df8626f", size = 2227443, upload-time = "2025-07-01T09:14:39.344Z" }, + { url = "https://files.pythonhosted.org/packages/01/f4/91d5b3ffa718df2f53b0dc109877993e511f4fd055d7e9508682e8aba092/pillow-11.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:ec1ee50470b0d050984394423d96325b744d55c701a439d2bd66089bff963d3c", size = 5278474, upload-time = "2025-07-01T09:14:41.843Z" }, + { url = "https://files.pythonhosted.org/packages/f9/0e/37d7d3eca6c879fbd9dba21268427dffda1ab00d4eb05b32923d4fbe3b12/pillow-11.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7db51d222548ccfd274e4572fdbf3e810a5e66b00608862f947b163e613b67dd", size = 4686038, upload-time = "2025-07-01T09:14:44.008Z" }, + { url = "https://files.pythonhosted.org/packages/ff/b0/3426e5c7f6565e752d81221af9d3676fdbb4f352317ceafd42899aaf5d8a/pillow-11.3.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2d6fcc902a24ac74495df63faad1884282239265c6839a0a6416d33faedfae7e", size = 5864407, upload-time = "2025-07-03T13:10:15.628Z" }, + { url = "https://files.pythonhosted.org/packages/fc/c1/c6c423134229f2a221ee53f838d4be9d82bab86f7e2f8e75e47b6bf6cd77/pillow-11.3.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f0f5d8f4a08090c6d6d578351a2b91acf519a54986c055af27e7a93feae6d3f1", size = 7639094, upload-time = "2025-07-03T13:10:21.857Z" }, + { url = "https://files.pythonhosted.org/packages/ba/c9/09e6746630fe6372c67c648ff9deae52a2bc20897d51fa293571977ceb5d/pillow-11.3.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c37d8ba9411d6003bba9e518db0db0c58a680ab9fe5179f040b0463644bc9805", size = 5973503, upload-time = "2025-07-01T09:14:45.698Z" }, + { url = "https://files.pythonhosted.org/packages/d5/1c/a2a29649c0b1983d3ef57ee87a66487fdeb45132df66ab30dd37f7dbe162/pillow-11.3.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:13f87d581e71d9189ab21fe0efb5a23e9f28552d5be6979e84001d3b8505abe8", size = 6642574, upload-time = "2025-07-01T09:14:47.415Z" }, + { url = "https://files.pythonhosted.org/packages/36/de/d5cc31cc4b055b6c6fd990e3e7f0f8aaf36229a2698501bcb0cdf67c7146/pillow-11.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:023f6d2d11784a465f09fd09a34b150ea4672e85fb3d05931d89f373ab14abb2", size = 6084060, upload-time = "2025-07-01T09:14:49.636Z" }, + { url = "https://files.pythonhosted.org/packages/d5/ea/502d938cbaeec836ac28a9b730193716f0114c41325db428e6b280513f09/pillow-11.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:45dfc51ac5975b938e9809451c51734124e73b04d0f0ac621649821a63852e7b", size = 6721407, upload-time = "2025-07-01T09:14:51.962Z" }, + { url = "https://files.pythonhosted.org/packages/45/9c/9c5e2a73f125f6cbc59cc7087c8f2d649a7ae453f83bd0362ff7c9e2aee2/pillow-11.3.0-cp313-cp313-win32.whl", hash = "sha256:a4d336baed65d50d37b88ca5b60c0fa9d81e3a87d4a7930d3880d1624d5b31f3", size = 6273841, upload-time = "2025-07-01T09:14:54.142Z" }, + { url = "https://files.pythonhosted.org/packages/23/85/397c73524e0cd212067e0c969aa245b01d50183439550d24d9f55781b776/pillow-11.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:0bce5c4fd0921f99d2e858dc4d4d64193407e1b99478bc5cacecba2311abde51", size = 6978450, upload-time = "2025-07-01T09:14:56.436Z" }, + { url = "https://files.pythonhosted.org/packages/17/d2/622f4547f69cd173955194b78e4d19ca4935a1b0f03a302d655c9f6aae65/pillow-11.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:1904e1264881f682f02b7f8167935cce37bc97db457f8e7849dc3a6a52b99580", size = 2423055, upload-time = "2025-07-01T09:14:58.072Z" }, + { url = "https://files.pythonhosted.org/packages/dd/80/a8a2ac21dda2e82480852978416cfacd439a4b490a501a288ecf4fe2532d/pillow-11.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:4c834a3921375c48ee6b9624061076bc0a32a60b5532b322cc0ea64e639dd50e", size = 5281110, upload-time = "2025-07-01T09:14:59.79Z" }, + { url = "https://files.pythonhosted.org/packages/44/d6/b79754ca790f315918732e18f82a8146d33bcd7f4494380457ea89eb883d/pillow-11.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5e05688ccef30ea69b9317a9ead994b93975104a677a36a8ed8106be9260aa6d", size = 4689547, upload-time = "2025-07-01T09:15:01.648Z" }, + { url = "https://files.pythonhosted.org/packages/49/20/716b8717d331150cb00f7fdd78169c01e8e0c219732a78b0e59b6bdb2fd6/pillow-11.3.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1019b04af07fc0163e2810167918cb5add8d74674b6267616021ab558dc98ced", size = 5901554, upload-time = "2025-07-03T13:10:27.018Z" }, + { url = "https://files.pythonhosted.org/packages/74/cf/a9f3a2514a65bb071075063a96f0a5cf949c2f2fce683c15ccc83b1c1cab/pillow-11.3.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f944255db153ebb2b19c51fe85dd99ef0ce494123f21b9db4877ffdfc5590c7c", size = 7669132, upload-time = "2025-07-03T13:10:33.01Z" }, + { url = "https://files.pythonhosted.org/packages/98/3c/da78805cbdbee9cb43efe8261dd7cc0b4b93f2ac79b676c03159e9db2187/pillow-11.3.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1f85acb69adf2aaee8b7da124efebbdb959a104db34d3a2cb0f3793dbae422a8", size = 6005001, upload-time = "2025-07-01T09:15:03.365Z" }, + { url = "https://files.pythonhosted.org/packages/6c/fa/ce044b91faecf30e635321351bba32bab5a7e034c60187fe9698191aef4f/pillow-11.3.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:05f6ecbeff5005399bb48d198f098a9b4b6bdf27b8487c7f38ca16eeb070cd59", size = 6668814, upload-time = "2025-07-01T09:15:05.655Z" }, + { url = "https://files.pythonhosted.org/packages/7b/51/90f9291406d09bf93686434f9183aba27b831c10c87746ff49f127ee80cb/pillow-11.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:a7bc6e6fd0395bc052f16b1a8670859964dbd7003bd0af2ff08342eb6e442cfe", size = 6113124, upload-time = "2025-07-01T09:15:07.358Z" }, + { url = "https://files.pythonhosted.org/packages/cd/5a/6fec59b1dfb619234f7636d4157d11fb4e196caeee220232a8d2ec48488d/pillow-11.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:83e1b0161c9d148125083a35c1c5a89db5b7054834fd4387499e06552035236c", size = 6747186, upload-time = "2025-07-01T09:15:09.317Z" }, + { url = "https://files.pythonhosted.org/packages/49/6b/00187a044f98255225f172de653941e61da37104a9ea60e4f6887717e2b5/pillow-11.3.0-cp313-cp313t-win32.whl", hash = "sha256:2a3117c06b8fb646639dce83694f2f9eac405472713fcb1ae887469c0d4f6788", size = 6277546, upload-time = "2025-07-01T09:15:11.311Z" }, + { url = "https://files.pythonhosted.org/packages/e8/5c/6caaba7e261c0d75bab23be79f1d06b5ad2a2ae49f028ccec801b0e853d6/pillow-11.3.0-cp313-cp313t-win_amd64.whl", hash = "sha256:857844335c95bea93fb39e0fa2726b4d9d758850b34075a7e3ff4f4fa3aa3b31", size = 6985102, upload-time = "2025-07-01T09:15:13.164Z" }, + { url = "https://files.pythonhosted.org/packages/f3/7e/b623008460c09a0cb38263c93b828c666493caee2eb34ff67f778b87e58c/pillow-11.3.0-cp313-cp313t-win_arm64.whl", hash = "sha256:8797edc41f3e8536ae4b10897ee2f637235c94f27404cac7297f7b607dd0716e", size = 2424803, upload-time = "2025-07-01T09:15:15.695Z" }, + { url = "https://files.pythonhosted.org/packages/73/f4/04905af42837292ed86cb1b1dabe03dce1edc008ef14c473c5c7e1443c5d/pillow-11.3.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:d9da3df5f9ea2a89b81bb6087177fb1f4d1c7146d583a3fe5c672c0d94e55e12", size = 5278520, upload-time = "2025-07-01T09:15:17.429Z" }, + { url = "https://files.pythonhosted.org/packages/41/b0/33d79e377a336247df6348a54e6d2a2b85d644ca202555e3faa0cf811ecc/pillow-11.3.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:0b275ff9b04df7b640c59ec5a3cb113eefd3795a8df80bac69646ef699c6981a", size = 4686116, upload-time = "2025-07-01T09:15:19.423Z" }, + { url = "https://files.pythonhosted.org/packages/49/2d/ed8bc0ab219ae8768f529597d9509d184fe8a6c4741a6864fea334d25f3f/pillow-11.3.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0743841cabd3dba6a83f38a92672cccbd69af56e3e91777b0ee7f4dba4385632", size = 5864597, upload-time = "2025-07-03T13:10:38.404Z" }, + { url = "https://files.pythonhosted.org/packages/b5/3d/b932bb4225c80b58dfadaca9d42d08d0b7064d2d1791b6a237f87f661834/pillow-11.3.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2465a69cf967b8b49ee1b96d76718cd98c4e925414ead59fdf75cf0fd07df673", size = 7638246, upload-time = "2025-07-03T13:10:44.987Z" }, + { url = "https://files.pythonhosted.org/packages/09/b5/0487044b7c096f1b48f0d7ad416472c02e0e4bf6919541b111efd3cae690/pillow-11.3.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41742638139424703b4d01665b807c6468e23e699e8e90cffefe291c5832b027", size = 5973336, upload-time = "2025-07-01T09:15:21.237Z" }, + { url = "https://files.pythonhosted.org/packages/a8/2d/524f9318f6cbfcc79fbc004801ea6b607ec3f843977652fdee4857a7568b/pillow-11.3.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:93efb0b4de7e340d99057415c749175e24c8864302369e05914682ba642e5d77", size = 6642699, upload-time = "2025-07-01T09:15:23.186Z" }, + { url = "https://files.pythonhosted.org/packages/6f/d2/a9a4f280c6aefedce1e8f615baaa5474e0701d86dd6f1dede66726462bbd/pillow-11.3.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7966e38dcd0fa11ca390aed7c6f20454443581d758242023cf36fcb319b1a874", size = 6083789, upload-time = "2025-07-01T09:15:25.1Z" }, + { url = "https://files.pythonhosted.org/packages/fe/54/86b0cd9dbb683a9d5e960b66c7379e821a19be4ac5810e2e5a715c09a0c0/pillow-11.3.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:98a9afa7b9007c67ed84c57c9e0ad86a6000da96eaa638e4f8abe5b65ff83f0a", size = 6720386, upload-time = "2025-07-01T09:15:27.378Z" }, + { url = "https://files.pythonhosted.org/packages/e7/95/88efcaf384c3588e24259c4203b909cbe3e3c2d887af9e938c2022c9dd48/pillow-11.3.0-cp314-cp314-win32.whl", hash = "sha256:02a723e6bf909e7cea0dac1b0e0310be9d7650cd66222a5f1c571455c0a45214", size = 6370911, upload-time = "2025-07-01T09:15:29.294Z" }, + { url = "https://files.pythonhosted.org/packages/2e/cc/934e5820850ec5eb107e7b1a72dd278140731c669f396110ebc326f2a503/pillow-11.3.0-cp314-cp314-win_amd64.whl", hash = "sha256:a418486160228f64dd9e9efcd132679b7a02a5f22c982c78b6fc7dab3fefb635", size = 7117383, upload-time = "2025-07-01T09:15:31.128Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e9/9c0a616a71da2a5d163aa37405e8aced9a906d574b4a214bede134e731bc/pillow-11.3.0-cp314-cp314-win_arm64.whl", hash = "sha256:155658efb5e044669c08896c0c44231c5e9abcaadbc5cd3648df2f7c0b96b9a6", size = 2511385, upload-time = "2025-07-01T09:15:33.328Z" }, + { url = "https://files.pythonhosted.org/packages/1a/33/c88376898aff369658b225262cd4f2659b13e8178e7534df9e6e1fa289f6/pillow-11.3.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:59a03cdf019efbfeeed910bf79c7c93255c3d54bc45898ac2a4140071b02b4ae", size = 5281129, upload-time = "2025-07-01T09:15:35.194Z" }, + { url = "https://files.pythonhosted.org/packages/1f/70/d376247fb36f1844b42910911c83a02d5544ebd2a8bad9efcc0f707ea774/pillow-11.3.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f8a5827f84d973d8636e9dc5764af4f0cf2318d26744b3d902931701b0d46653", size = 4689580, upload-time = "2025-07-01T09:15:37.114Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1c/537e930496149fbac69efd2fc4329035bbe2e5475b4165439e3be9cb183b/pillow-11.3.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:ee92f2fd10f4adc4b43d07ec5e779932b4eb3dbfbc34790ada5a6669bc095aa6", size = 5902860, upload-time = "2025-07-03T13:10:50.248Z" }, + { url = "https://files.pythonhosted.org/packages/bd/57/80f53264954dcefeebcf9dae6e3eb1daea1b488f0be8b8fef12f79a3eb10/pillow-11.3.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c96d333dcf42d01f47b37e0979b6bd73ec91eae18614864622d9b87bbd5bbf36", size = 7670694, upload-time = "2025-07-03T13:10:56.432Z" }, + { url = "https://files.pythonhosted.org/packages/70/ff/4727d3b71a8578b4587d9c276e90efad2d6fe0335fd76742a6da08132e8c/pillow-11.3.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4c96f993ab8c98460cd0c001447bff6194403e8b1d7e149ade5f00594918128b", size = 6005888, upload-time = "2025-07-01T09:15:39.436Z" }, + { url = "https://files.pythonhosted.org/packages/05/ae/716592277934f85d3be51d7256f3636672d7b1abfafdc42cf3f8cbd4b4c8/pillow-11.3.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:41342b64afeba938edb034d122b2dda5db2139b9a4af999729ba8818e0056477", size = 6670330, upload-time = "2025-07-01T09:15:41.269Z" }, + { url = "https://files.pythonhosted.org/packages/e7/bb/7fe6cddcc8827b01b1a9766f5fdeb7418680744f9082035bdbabecf1d57f/pillow-11.3.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:068d9c39a2d1b358eb9f245ce7ab1b5c3246c7c8c7d9ba58cfa5b43146c06e50", size = 6114089, upload-time = "2025-07-01T09:15:43.13Z" }, + { url = "https://files.pythonhosted.org/packages/8b/f5/06bfaa444c8e80f1a8e4bff98da9c83b37b5be3b1deaa43d27a0db37ef84/pillow-11.3.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a1bc6ba083b145187f648b667e05a2534ecc4b9f2784c2cbe3089e44868f2b9b", size = 6748206, upload-time = "2025-07-01T09:15:44.937Z" }, + { url = "https://files.pythonhosted.org/packages/f0/77/bc6f92a3e8e6e46c0ca78abfffec0037845800ea38c73483760362804c41/pillow-11.3.0-cp314-cp314t-win32.whl", hash = "sha256:118ca10c0d60b06d006be10a501fd6bbdfef559251ed31b794668ed569c87e12", size = 6377370, upload-time = "2025-07-01T09:15:46.673Z" }, + { url = "https://files.pythonhosted.org/packages/4a/82/3a721f7d69dca802befb8af08b7c79ebcab461007ce1c18bd91a5d5896f9/pillow-11.3.0-cp314-cp314t-win_amd64.whl", hash = "sha256:8924748b688aa210d79883357d102cd64690e56b923a186f35a82cbc10f997db", size = 7121500, upload-time = "2025-07-01T09:15:48.512Z" }, + { url = "https://files.pythonhosted.org/packages/89/c7/5572fa4a3f45740eaab6ae86fcdf7195b55beac1371ac8c619d880cfe948/pillow-11.3.0-cp314-cp314t-win_arm64.whl", hash = "sha256:79ea0d14d3ebad43ec77ad5272e6ff9bba5b679ef73375ea760261207fa8e0aa", size = 2512835, upload-time = "2025-07-01T09:15:50.399Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8e/9c089f01677d1264ab8648352dcb7773f37da6ad002542760c80107da816/pillow-11.3.0-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:48d254f8a4c776de343051023eb61ffe818299eeac478da55227d96e241de53f", size = 5316478, upload-time = "2025-07-01T09:15:52.209Z" }, + { url = "https://files.pythonhosted.org/packages/b5/a9/5749930caf674695867eb56a581e78eb5f524b7583ff10b01b6e5048acb3/pillow-11.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:7aee118e30a4cf54fdd873bd3a29de51e29105ab11f9aad8c32123f58c8f8081", size = 4686522, upload-time = "2025-07-01T09:15:54.162Z" }, + { url = "https://files.pythonhosted.org/packages/43/46/0b85b763eb292b691030795f9f6bb6fcaf8948c39413c81696a01c3577f7/pillow-11.3.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:23cff760a9049c502721bdb743a7cb3e03365fafcdfc2ef9784610714166e5a4", size = 5853376, upload-time = "2025-07-03T13:11:01.066Z" }, + { url = "https://files.pythonhosted.org/packages/5e/c6/1a230ec0067243cbd60bc2dad5dc3ab46a8a41e21c15f5c9b52b26873069/pillow-11.3.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:6359a3bc43f57d5b375d1ad54a0074318a0844d11b76abccf478c37c986d3cfc", size = 7626020, upload-time = "2025-07-03T13:11:06.479Z" }, + { url = "https://files.pythonhosted.org/packages/63/dd/f296c27ffba447bfad76c6a0c44c1ea97a90cb9472b9304c94a732e8dbfb/pillow-11.3.0-cp39-cp39-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:092c80c76635f5ecb10f3f83d76716165c96f5229addbd1ec2bdbbda7d496e06", size = 5956732, upload-time = "2025-07-01T09:15:56.111Z" }, + { url = "https://files.pythonhosted.org/packages/a5/a0/98a3630f0b57f77bae67716562513d3032ae70414fcaf02750279c389a9e/pillow-11.3.0-cp39-cp39-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:cadc9e0ea0a2431124cde7e1697106471fc4c1da01530e679b2391c37d3fbb3a", size = 6624404, upload-time = "2025-07-01T09:15:58.245Z" }, + { url = "https://files.pythonhosted.org/packages/de/e6/83dfba5646a290edd9a21964da07674409e410579c341fc5b8f7abd81620/pillow-11.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:6a418691000f2a418c9135a7cf0d797c1bb7d9a485e61fe8e7722845b95ef978", size = 6067760, upload-time = "2025-07-01T09:16:00.003Z" }, + { url = "https://files.pythonhosted.org/packages/bc/41/15ab268fe6ee9a2bc7391e2bbb20a98d3974304ab1a406a992dcb297a370/pillow-11.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:97afb3a00b65cc0804d1c7abddbf090a81eaac02768af58cbdcaaa0a931e0b6d", size = 6700534, upload-time = "2025-07-01T09:16:02.29Z" }, + { url = "https://files.pythonhosted.org/packages/64/79/6d4f638b288300bed727ff29f2a3cb63db054b33518a95f27724915e3fbc/pillow-11.3.0-cp39-cp39-win32.whl", hash = "sha256:ea944117a7974ae78059fcc1800e5d3295172bb97035c0c1d9345fca1419da71", size = 6277091, upload-time = "2025-07-01T09:16:04.4Z" }, + { url = "https://files.pythonhosted.org/packages/46/05/4106422f45a05716fd34ed21763f8ec182e8ea00af6e9cb05b93a247361a/pillow-11.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:e5c5858ad8ec655450a7c7df532e9842cf8df7cc349df7225c60d5d348c8aada", size = 6986091, upload-time = "2025-07-01T09:16:06.342Z" }, + { url = "https://files.pythonhosted.org/packages/63/c6/287fd55c2c12761d0591549d48885187579b7c257bef0c6660755b0b59ae/pillow-11.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:6abdbfd3aea42be05702a8dd98832329c167ee84400a1d1f61ab11437f1717eb", size = 2422632, upload-time = "2025-07-01T09:16:08.142Z" }, + { url = "https://files.pythonhosted.org/packages/6f/8b/209bd6b62ce8367f47e68a218bffac88888fdf2c9fcf1ecadc6c3ec1ebc7/pillow-11.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:3cee80663f29e3843b68199b9d6f4f54bd1d4a6b59bdd91bceefc51238bcb967", size = 5270556, upload-time = "2025-07-01T09:16:09.961Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e6/231a0b76070c2cfd9e260a7a5b504fb72da0a95279410fa7afd99d9751d6/pillow-11.3.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:b5f56c3f344f2ccaf0dd875d3e180f631dc60a51b314295a3e681fe8cf851fbe", size = 4654625, upload-time = "2025-07-01T09:16:11.913Z" }, + { url = "https://files.pythonhosted.org/packages/13/f4/10cf94fda33cb12765f2397fc285fa6d8eb9c29de7f3185165b702fc7386/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e67d793d180c9df62f1f40aee3accca4829d3794c95098887edc18af4b8b780c", size = 4874207, upload-time = "2025-07-03T13:11:10.201Z" }, + { url = "https://files.pythonhosted.org/packages/72/c9/583821097dc691880c92892e8e2d41fe0a5a3d6021f4963371d2f6d57250/pillow-11.3.0-pp310-pypy310_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d000f46e2917c705e9fb93a3606ee4a819d1e3aa7a9b442f6444f07e77cf5e25", size = 6583939, upload-time = "2025-07-03T13:11:15.68Z" }, + { url = "https://files.pythonhosted.org/packages/3b/8e/5c9d410f9217b12320efc7c413e72693f48468979a013ad17fd690397b9a/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:527b37216b6ac3a12d7838dc3bd75208ec57c1c6d11ef01902266a5a0c14fc27", size = 4957166, upload-time = "2025-07-01T09:16:13.74Z" }, + { url = "https://files.pythonhosted.org/packages/62/bb/78347dbe13219991877ffb3a91bf09da8317fbfcd4b5f9140aeae020ad71/pillow-11.3.0-pp310-pypy310_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:be5463ac478b623b9dd3937afd7fb7ab3d79dd290a28e2b6df292dc75063eb8a", size = 5581482, upload-time = "2025-07-01T09:16:16.107Z" }, + { url = "https://files.pythonhosted.org/packages/d9/28/1000353d5e61498aaeaaf7f1e4b49ddb05f2c6575f9d4f9f914a3538b6e1/pillow-11.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8dc70ca24c110503e16918a658b869019126ecfe03109b754c402daff12b3d9f", size = 6984596, upload-time = "2025-07-01T09:16:18.07Z" }, + { url = "https://files.pythonhosted.org/packages/9e/e3/6fa84033758276fb31da12e5fb66ad747ae83b93c67af17f8c6ff4cc8f34/pillow-11.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7c8ec7a017ad1bd562f93dbd8505763e688d388cde6e4a010ae1486916e713e6", size = 5270566, upload-time = "2025-07-01T09:16:19.801Z" }, + { url = "https://files.pythonhosted.org/packages/5b/ee/e8d2e1ab4892970b561e1ba96cbd59c0d28cf66737fc44abb2aec3795a4e/pillow-11.3.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:9ab6ae226de48019caa8074894544af5b53a117ccb9d3b3dcb2871464c829438", size = 4654618, upload-time = "2025-07-01T09:16:21.818Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6d/17f80f4e1f0761f02160fc433abd4109fa1548dcfdca46cfdadaf9efa565/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fe27fb049cdcca11f11a7bfda64043c37b30e6b91f10cb5bab275806c32f6ab3", size = 4874248, upload-time = "2025-07-03T13:11:20.738Z" }, + { url = "https://files.pythonhosted.org/packages/de/5f/c22340acd61cef960130585bbe2120e2fd8434c214802f07e8c03596b17e/pillow-11.3.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:465b9e8844e3c3519a983d58b80be3f668e2a7a5db97f2784e7079fbc9f9822c", size = 6583963, upload-time = "2025-07-03T13:11:26.283Z" }, + { url = "https://files.pythonhosted.org/packages/31/5e/03966aedfbfcbb4d5f8aa042452d3361f325b963ebbadddac05b122e47dd/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5418b53c0d59b3824d05e029669efa023bbef0f3e92e75ec8428f3799487f361", size = 4957170, upload-time = "2025-07-01T09:16:23.762Z" }, + { url = "https://files.pythonhosted.org/packages/cc/2d/e082982aacc927fc2cab48e1e731bdb1643a1406acace8bed0900a61464e/pillow-11.3.0-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:504b6f59505f08ae014f724b6207ff6222662aab5cc9542577fb084ed0676ac7", size = 5581505, upload-time = "2025-07-01T09:16:25.593Z" }, + { url = "https://files.pythonhosted.org/packages/34/e7/ae39f538fd6844e982063c3a5e4598b8ced43b9633baa3a85ef33af8c05c/pillow-11.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:c84d689db21a1c397d001aa08241044aa2069e7587b398c8cc63020390b1c1b8", size = 6984598, upload-time = "2025-07-01T09:16:27.732Z" }, +] + +[[package]] +name = "pillow" +version = "12.1.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/1f/42/5c74462b4fd957fcd7b13b04fb3205ff8349236ea74c7c375766d6c82288/pillow-12.1.1.tar.gz", hash = "sha256:9ad8fa5937ab05218e2b6a4cff30295ad35afd2f83ac592e68c0d871bb0fdbc4", size = 46980264, upload-time = "2026-02-11T04:23:07.146Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1d/30/5bd3d794762481f8c8ae9c80e7b76ecea73b916959eb587521358ef0b2f9/pillow-12.1.1-cp310-cp310-macosx_10_10_x86_64.whl", hash = "sha256:1f1625b72740fdda5d77b4def688eb8fd6490975d06b909fd19f13f391e077e0", size = 5304099, upload-time = "2026-02-11T04:20:06.13Z" }, + { url = "https://files.pythonhosted.org/packages/bd/c1/aab9e8f3eeb4490180e357955e15c2ef74b31f64790ff356c06fb6cf6d84/pillow-12.1.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:178aa072084bd88ec759052feca8e56cbb14a60b39322b99a049e58090479713", size = 4657880, upload-time = "2026-02-11T04:20:09.291Z" }, + { url = "https://files.pythonhosted.org/packages/f1/0a/9879e30d56815ad529d3985aeff5af4964202425c27261a6ada10f7cbf53/pillow-12.1.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b66e95d05ba806247aaa1561f080abc7975daf715c30780ff92a20e4ec546e1b", size = 6222587, upload-time = "2026-02-11T04:20:10.82Z" }, + { url = "https://files.pythonhosted.org/packages/5a/5f/a1b72ff7139e4f89014e8d451442c74a774d5c43cd938fb0a9f878576b37/pillow-12.1.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:89c7e895002bbe49cdc5426150377cbbc04767d7547ed145473f496dfa40408b", size = 8027678, upload-time = "2026-02-11T04:20:12.455Z" }, + { url = "https://files.pythonhosted.org/packages/e2/c2/c7cb187dac79a3d22c3ebeae727abee01e077c8c7d930791dc592f335153/pillow-12.1.1-cp310-cp310-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3a5cbdcddad0af3da87cb16b60d23648bc3b51967eb07223e9fed77a82b457c4", size = 6335777, upload-time = "2026-02-11T04:20:14.441Z" }, + { url = "https://files.pythonhosted.org/packages/0c/7b/f9b09a7804ec7336effb96c26d37c29d27225783dc1501b7d62dcef6ae25/pillow-12.1.1-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9f51079765661884a486727f0729d29054242f74b46186026582b4e4769918e4", size = 7027140, upload-time = "2026-02-11T04:20:16.387Z" }, + { url = "https://files.pythonhosted.org/packages/98/b2/2fa3c391550bd421b10849d1a2144c44abcd966daadd2f7c12e19ea988c4/pillow-12.1.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:99c1506ea77c11531d75e3a412832a13a71c7ebc8192ab9e4b2e355555920e3e", size = 6449855, upload-time = "2026-02-11T04:20:18.554Z" }, + { url = "https://files.pythonhosted.org/packages/96/ff/9caf4b5b950c669263c39e96c78c0d74a342c71c4f43fd031bb5cb7ceac9/pillow-12.1.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36341d06738a9f66c8287cf8b876d24b18db9bd8740fa0672c74e259ad408cff", size = 7151329, upload-time = "2026-02-11T04:20:20.646Z" }, + { url = "https://files.pythonhosted.org/packages/7b/f8/4b24841f582704da675ca535935bccb32b00a6da1226820845fac4a71136/pillow-12.1.1-cp310-cp310-win32.whl", hash = "sha256:6c52f062424c523d6c4db85518774cc3d50f5539dd6eed32b8f6229b26f24d40", size = 6325574, upload-time = "2026-02-11T04:20:22.43Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/9f6b01c0881d7036063aa6612ef04c0e2cad96be21325a1e92d0203f8e91/pillow-12.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:c6008de247150668a705a6338156efb92334113421ceecf7438a12c9a12dab23", size = 7032347, upload-time = "2026-02-11T04:20:23.932Z" }, + { url = "https://files.pythonhosted.org/packages/79/13/c7922edded3dcdaf10c59297540b72785620abc0538872c819915746757d/pillow-12.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:1a9b0ee305220b392e1124a764ee4265bd063e54a751a6b62eff69992f457fa9", size = 2453457, upload-time = "2026-02-11T04:20:25.392Z" }, + { url = "https://files.pythonhosted.org/packages/2b/46/5da1ec4a5171ee7bf1a0efa064aba70ba3d6e0788ce3f5acd1375d23c8c0/pillow-12.1.1-cp311-cp311-macosx_10_10_x86_64.whl", hash = "sha256:e879bb6cd5c73848ef3b2b48b8af9ff08c5b71ecda8048b7dd22d8a33f60be32", size = 5304084, upload-time = "2026-02-11T04:20:27.501Z" }, + { url = "https://files.pythonhosted.org/packages/78/93/a29e9bc02d1cf557a834da780ceccd54e02421627200696fcf805ebdc3fb/pillow-12.1.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:365b10bb9417dd4498c0e3b128018c4a624dc11c7b97d8cc54effe3b096f4c38", size = 4657866, upload-time = "2026-02-11T04:20:29.827Z" }, + { url = "https://files.pythonhosted.org/packages/13/84/583a4558d492a179d31e4aae32eadce94b9acf49c0337c4ce0b70e0a01f2/pillow-12.1.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d4ce8e329c93845720cd2014659ca67eac35f6433fd3050393d85f3ecef0dad5", size = 6232148, upload-time = "2026-02-11T04:20:31.329Z" }, + { url = "https://files.pythonhosted.org/packages/d5/e2/53c43334bbbb2d3b938978532fbda8e62bb6e0b23a26ce8592f36bcc4987/pillow-12.1.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc354a04072b765eccf2204f588a7a532c9511e8b9c7f900e1b64e3e33487090", size = 8038007, upload-time = "2026-02-11T04:20:34.225Z" }, + { url = "https://files.pythonhosted.org/packages/b8/a6/3d0e79c8a9d58150dd98e199d7c1c56861027f3829a3a60b3c2784190180/pillow-12.1.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7e7976bf1910a8116b523b9f9f58bf410f3e8aa330cd9a2bb2953f9266ab49af", size = 6345418, upload-time = "2026-02-11T04:20:35.858Z" }, + { url = "https://files.pythonhosted.org/packages/a2/c8/46dfeac5825e600579157eea177be43e2f7ff4a99da9d0d0a49533509ac5/pillow-12.1.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:597bd9c8419bc7c6af5604e55847789b69123bbe25d65cc6ad3012b4f3c98d8b", size = 7034590, upload-time = "2026-02-11T04:20:37.91Z" }, + { url = "https://files.pythonhosted.org/packages/af/bf/e6f65d3db8a8bbfeaf9e13cc0417813f6319863a73de934f14b2229ada18/pillow-12.1.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:2c1fc0f2ca5f96a3c8407e41cca26a16e46b21060fe6d5b099d2cb01412222f5", size = 6458655, upload-time = "2026-02-11T04:20:39.496Z" }, + { url = "https://files.pythonhosted.org/packages/f9/c2/66091f3f34a25894ca129362e510b956ef26f8fb67a0e6417bc5744e56f1/pillow-12.1.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:578510d88c6229d735855e1f278aa305270438d36a05031dfaae5067cc8eb04d", size = 7159286, upload-time = "2026-02-11T04:20:41.139Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5a/24bc8eb526a22f957d0cec6243146744966d40857e3d8deb68f7902ca6c1/pillow-12.1.1-cp311-cp311-win32.whl", hash = "sha256:7311c0a0dcadb89b36b7025dfd8326ecfa36964e29913074d47382706e516a7c", size = 6328663, upload-time = "2026-02-11T04:20:43.184Z" }, + { url = "https://files.pythonhosted.org/packages/31/03/bef822e4f2d8f9d7448c133d0a18185d3cce3e70472774fffefe8b0ed562/pillow-12.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:fbfa2a7c10cc2623f412753cddf391c7f971c52ca40a3f65dc5039b2939e8563", size = 7031448, upload-time = "2026-02-11T04:20:44.696Z" }, + { url = "https://files.pythonhosted.org/packages/49/70/f76296f53610bd17b2e7d31728b8b7825e3ac3b5b3688b51f52eab7c0818/pillow-12.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:b81b5e3511211631b3f672a595e3221252c90af017e399056d0faabb9538aa80", size = 2453651, upload-time = "2026-02-11T04:20:46.243Z" }, + { url = "https://files.pythonhosted.org/packages/07/d3/8df65da0d4df36b094351dce696f2989bec731d4f10e743b1c5f4da4d3bf/pillow-12.1.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ab323b787d6e18b3d91a72fc99b1a2c28651e4358749842b8f8dfacd28ef2052", size = 5262803, upload-time = "2026-02-11T04:20:47.653Z" }, + { url = "https://files.pythonhosted.org/packages/d6/71/5026395b290ff404b836e636f51d7297e6c83beceaa87c592718747e670f/pillow-12.1.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:adebb5bee0f0af4909c30db0d890c773d1a92ffe83da908e2e9e720f8edf3984", size = 4657601, upload-time = "2026-02-11T04:20:49.328Z" }, + { url = "https://files.pythonhosted.org/packages/b1/2e/1001613d941c67442f745aff0f7cc66dd8df9a9c084eb497e6a543ee6f7e/pillow-12.1.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bb66b7cc26f50977108790e2456b7921e773f23db5630261102233eb355a3b79", size = 6234995, upload-time = "2026-02-11T04:20:51.032Z" }, + { url = "https://files.pythonhosted.org/packages/07/26/246ab11455b2549b9233dbd44d358d033a2f780fa9007b61a913c5b2d24e/pillow-12.1.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:aee2810642b2898bb187ced9b349e95d2a7272930796e022efaf12e99dccd293", size = 8045012, upload-time = "2026-02-11T04:20:52.882Z" }, + { url = "https://files.pythonhosted.org/packages/b2/8b/07587069c27be7535ac1fe33874e32de118fbd34e2a73b7f83436a88368c/pillow-12.1.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a0b1cd6232e2b618adcc54d9882e4e662a089d5768cd188f7c245b4c8c44a397", size = 6349638, upload-time = "2026-02-11T04:20:54.444Z" }, + { url = "https://files.pythonhosted.org/packages/ff/79/6df7b2ee763d619cda2fb4fea498e5f79d984dae304d45a8999b80d6cf5c/pillow-12.1.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7aac39bcf8d4770d089588a2e1dd111cbaa42df5a94be3114222057d68336bd0", size = 7041540, upload-time = "2026-02-11T04:20:55.97Z" }, + { url = "https://files.pythonhosted.org/packages/2c/5e/2ba19e7e7236d7529f4d873bdaf317a318896bac289abebd4bb00ef247f0/pillow-12.1.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ab174cd7d29a62dd139c44bf74b698039328f45cb03b4596c43473a46656b2f3", size = 6462613, upload-time = "2026-02-11T04:20:57.542Z" }, + { url = "https://files.pythonhosted.org/packages/03/03/31216ec124bb5c3dacd74ce8efff4cc7f52643653bad4825f8f08c697743/pillow-12.1.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:339ffdcb7cbeaa08221cd401d517d4b1fe7a9ed5d400e4a8039719238620ca35", size = 7166745, upload-time = "2026-02-11T04:20:59.196Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e7/7c4552d80052337eb28653b617eafdef39adfb137c49dd7e831b8dc13bc5/pillow-12.1.1-cp312-cp312-win32.whl", hash = "sha256:5d1f9575a12bed9e9eedd9a4972834b08c97a352bd17955ccdebfeca5913fa0a", size = 6328823, upload-time = "2026-02-11T04:21:01.385Z" }, + { url = "https://files.pythonhosted.org/packages/3d/17/688626d192d7261bbbf98846fc98995726bddc2c945344b65bec3a29d731/pillow-12.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:21329ec8c96c6e979cd0dfd29406c40c1d52521a90544463057d2aaa937d66a6", size = 7033367, upload-time = "2026-02-11T04:21:03.536Z" }, + { url = "https://files.pythonhosted.org/packages/ed/fe/a0ef1f73f939b0eca03ee2c108d0043a87468664770612602c63266a43c4/pillow-12.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:af9a332e572978f0218686636610555ae3defd1633597be015ed50289a03c523", size = 2453811, upload-time = "2026-02-11T04:21:05.116Z" }, + { url = "https://files.pythonhosted.org/packages/d5/11/6db24d4bd7685583caeae54b7009584e38da3c3d4488ed4cd25b439de486/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:d242e8ac078781f1de88bf823d70c1a9b3c7950a44cdf4b7c012e22ccbcd8e4e", size = 4062689, upload-time = "2026-02-11T04:21:06.804Z" }, + { url = "https://files.pythonhosted.org/packages/33/c0/ce6d3b1fe190f0021203e0d9b5b99e57843e345f15f9ef22fcd43842fd21/pillow-12.1.1-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:02f84dfad02693676692746df05b89cf25597560db2857363a208e393429f5e9", size = 4138535, upload-time = "2026-02-11T04:21:08.452Z" }, + { url = "https://files.pythonhosted.org/packages/a0/c6/d5eb6a4fb32a3f9c21a8c7613ec706534ea1cf9f4b3663e99f0d83f6fca8/pillow-12.1.1-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:e65498daf4b583091ccbb2556c7000abf0f3349fcd57ef7adc9a84a394ed29f6", size = 3601364, upload-time = "2026-02-11T04:21:10.194Z" }, + { url = "https://files.pythonhosted.org/packages/14/a1/16c4b823838ba4c9c52c0e6bbda903a3fe5a1bdbf1b8eb4fff7156f3e318/pillow-12.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c6db3b84c87d48d0088943bf33440e0c42370b99b1c2a7989216f7b42eede60", size = 5262561, upload-time = "2026-02-11T04:21:11.742Z" }, + { url = "https://files.pythonhosted.org/packages/bb/ad/ad9dc98ff24f485008aa5cdedaf1a219876f6f6c42a4626c08bc4e80b120/pillow-12.1.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:8b7e5304e34942bf62e15184219a7b5ad4ff7f3bb5cca4d984f37df1a0e1aee2", size = 4657460, upload-time = "2026-02-11T04:21:13.786Z" }, + { url = "https://files.pythonhosted.org/packages/9e/1b/f1a4ea9a895b5732152789326202a82464d5254759fbacae4deea3069334/pillow-12.1.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:18e5bddd742a44b7e6b1e773ab5db102bd7a94c32555ba656e76d319d19c3850", size = 6232698, upload-time = "2026-02-11T04:21:15.949Z" }, + { url = "https://files.pythonhosted.org/packages/95/f4/86f51b8745070daf21fd2e5b1fe0eb35d4db9ca26e6d58366562fb56a743/pillow-12.1.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fc44ef1f3de4f45b50ccf9136999d71abb99dca7706bc75d222ed350b9fd2289", size = 8041706, upload-time = "2026-02-11T04:21:17.723Z" }, + { url = "https://files.pythonhosted.org/packages/29/9b/d6ecd956bb1266dd1045e995cce9b8d77759e740953a1c9aad9502a0461e/pillow-12.1.1-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5a8eb7ed8d4198bccbd07058416eeec51686b498e784eda166395a23eb99138e", size = 6346621, upload-time = "2026-02-11T04:21:19.547Z" }, + { url = "https://files.pythonhosted.org/packages/71/24/538bff45bde96535d7d998c6fed1a751c75ac7c53c37c90dc2601b243893/pillow-12.1.1-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47b94983da0c642de92ced1702c5b6c292a84bd3a8e1d1702ff923f183594717", size = 7038069, upload-time = "2026-02-11T04:21:21.378Z" }, + { url = "https://files.pythonhosted.org/packages/94/0e/58cb1a6bc48f746bc4cb3adb8cabff73e2742c92b3bf7a220b7cf69b9177/pillow-12.1.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:518a48c2aab7ce596d3bf79d0e275661b846e86e4d0e7dec34712c30fe07f02a", size = 6460040, upload-time = "2026-02-11T04:21:23.148Z" }, + { url = "https://files.pythonhosted.org/packages/6c/57/9045cb3ff11eeb6c1adce3b2d60d7d299d7b273a2e6c8381a524abfdc474/pillow-12.1.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:a550ae29b95c6dc13cf69e2c9dc5747f814c54eeb2e32d683e5e93af56caa029", size = 7164523, upload-time = "2026-02-11T04:21:25.01Z" }, + { url = "https://files.pythonhosted.org/packages/73/f2/9be9cb99f2175f0d4dbadd6616ce1bf068ee54a28277ea1bf1fbf729c250/pillow-12.1.1-cp313-cp313-win32.whl", hash = "sha256:a003d7422449f6d1e3a34e3dd4110c22148336918ddbfc6a32581cd54b2e0b2b", size = 6332552, upload-time = "2026-02-11T04:21:27.238Z" }, + { url = "https://files.pythonhosted.org/packages/3f/eb/b0834ad8b583d7d9d42b80becff092082a1c3c156bb582590fcc973f1c7c/pillow-12.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:344cf1e3dab3be4b1fa08e449323d98a2a3f819ad20f4b22e77a0ede31f0faa1", size = 7040108, upload-time = "2026-02-11T04:21:29.462Z" }, + { url = "https://files.pythonhosted.org/packages/d5/7d/fc09634e2aabdd0feabaff4a32f4a7d97789223e7c2042fd805ea4b4d2c2/pillow-12.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:5c0dd1636633e7e6a0afe7bf6a51a14992b7f8e60de5789018ebbdfae55b040a", size = 2453712, upload-time = "2026-02-11T04:21:31.072Z" }, + { url = "https://files.pythonhosted.org/packages/19/2a/b9d62794fc8a0dd14c1943df68347badbd5511103e0d04c035ffe5cf2255/pillow-12.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:0330d233c1a0ead844fc097a7d16c0abff4c12e856c0b325f231820fee1f39da", size = 5264880, upload-time = "2026-02-11T04:21:32.865Z" }, + { url = "https://files.pythonhosted.org/packages/26/9d/e03d857d1347fa5ed9247e123fcd2a97b6220e15e9cb73ca0a8d91702c6e/pillow-12.1.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:5dae5f21afb91322f2ff791895ddd8889e5e947ff59f71b46041c8ce6db790bc", size = 4660616, upload-time = "2026-02-11T04:21:34.97Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/8a6d22afd02570d30954e043f09c32772bfe143ba9285e2fdb11284952cd/pillow-12.1.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:2e0c664be47252947d870ac0d327fea7e63985a08794758aa8af5b6cb6ec0c9c", size = 6269008, upload-time = "2026-02-11T04:21:36.623Z" }, + { url = "https://files.pythonhosted.org/packages/3d/1d/6d875422c9f28a4a361f495a5f68d9de4a66941dc2c619103ca335fa6446/pillow-12.1.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:691ab2ac363b8217f7d31b3497108fb1f50faab2f75dfb03284ec2f217e87bf8", size = 8073226, upload-time = "2026-02-11T04:21:38.585Z" }, + { url = "https://files.pythonhosted.org/packages/a1/cd/134b0b6ee5eda6dc09e25e24b40fdafe11a520bc725c1d0bbaa5e00bf95b/pillow-12.1.1-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e9e8064fb1cc019296958595f6db671fba95209e3ceb0c4734c9baf97de04b20", size = 6380136, upload-time = "2026-02-11T04:21:40.562Z" }, + { url = "https://files.pythonhosted.org/packages/7a/a9/7628f013f18f001c1b98d8fffe3452f306a70dc6aba7d931019e0492f45e/pillow-12.1.1-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:472a8d7ded663e6162dafdf20015c486a7009483ca671cece7a9279b512fcb13", size = 7067129, upload-time = "2026-02-11T04:21:42.521Z" }, + { url = "https://files.pythonhosted.org/packages/1e/f8/66ab30a2193b277785601e82ee2d49f68ea575d9637e5e234faaa98efa4c/pillow-12.1.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:89b54027a766529136a06cfebeecb3a04900397a3590fd252160b888479517bf", size = 6491807, upload-time = "2026-02-11T04:21:44.22Z" }, + { url = "https://files.pythonhosted.org/packages/da/0b/a877a6627dc8318fdb84e357c5e1a758c0941ab1ddffdafd231983788579/pillow-12.1.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:86172b0831b82ce4f7877f280055892b31179e1576aa00d0df3bb1bbf8c3e524", size = 7190954, upload-time = "2026-02-11T04:21:46.114Z" }, + { url = "https://files.pythonhosted.org/packages/83/43/6f732ff85743cf746b1361b91665d9f5155e1483817f693f8d57ea93147f/pillow-12.1.1-cp313-cp313t-win32.whl", hash = "sha256:44ce27545b6efcf0fdbdceb31c9a5bdea9333e664cda58a7e674bb74608b3986", size = 6336441, upload-time = "2026-02-11T04:21:48.22Z" }, + { url = "https://files.pythonhosted.org/packages/3b/44/e865ef3986611bb75bfabdf94a590016ea327833f434558801122979cd0e/pillow-12.1.1-cp313-cp313t-win_amd64.whl", hash = "sha256:a285e3eb7a5a45a2ff504e31f4a8d1b12ef62e84e5411c6804a42197c1cf586c", size = 7045383, upload-time = "2026-02-11T04:21:50.015Z" }, + { url = "https://files.pythonhosted.org/packages/a8/c6/f4fb24268d0c6908b9f04143697ea18b0379490cb74ba9e8d41b898bd005/pillow-12.1.1-cp313-cp313t-win_arm64.whl", hash = "sha256:cc7d296b5ea4d29e6570dabeaed58d31c3fea35a633a69679fb03d7664f43fb3", size = 2456104, upload-time = "2026-02-11T04:21:51.633Z" }, + { url = "https://files.pythonhosted.org/packages/03/d0/bebb3ffbf31c5a8e97241476c4cf8b9828954693ce6744b4a2326af3e16b/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:417423db963cb4be8bac3fc1204fe61610f6abeed1580a7a2cbb2fbda20f12af", size = 4062652, upload-time = "2026-02-11T04:21:53.19Z" }, + { url = "https://files.pythonhosted.org/packages/2d/c0/0e16fb0addda4851445c28f8350d8c512f09de27bbb0d6d0bbf8b6709605/pillow-12.1.1-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:b957b71c6b2387610f556a7eb0828afbe40b4a98036fc0d2acfa5a44a0c2036f", size = 4138823, upload-time = "2026-02-11T04:22:03.088Z" }, + { url = "https://files.pythonhosted.org/packages/6b/fb/6170ec655d6f6bb6630a013dd7cf7bc218423d7b5fa9071bf63dc32175ae/pillow-12.1.1-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:097690ba1f2efdeb165a20469d59d8bb03c55fb6621eb2041a060ae8ea3e9642", size = 3601143, upload-time = "2026-02-11T04:22:04.909Z" }, + { url = "https://files.pythonhosted.org/packages/59/04/dc5c3f297510ba9a6837cbb318b87dd2b8f73eb41a43cc63767f65cb599c/pillow-12.1.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:2815a87ab27848db0321fb78c7f0b2c8649dee134b7f2b80c6a45c6831d75ccd", size = 5266254, upload-time = "2026-02-11T04:22:07.656Z" }, + { url = "https://files.pythonhosted.org/packages/05/30/5db1236b0d6313f03ebf97f5e17cda9ca060f524b2fcc875149a8360b21c/pillow-12.1.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:f7ed2c6543bad5a7d5530eb9e78c53132f93dfa44a28492db88b41cdab885202", size = 4657499, upload-time = "2026-02-11T04:22:09.613Z" }, + { url = "https://files.pythonhosted.org/packages/6f/18/008d2ca0eb612e81968e8be0bbae5051efba24d52debf930126d7eaacbba/pillow-12.1.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:652a2c9ccfb556235b2b501a3a7cf3742148cd22e04b5625c5fe057ea3e3191f", size = 6232137, upload-time = "2026-02-11T04:22:11.434Z" }, + { url = "https://files.pythonhosted.org/packages/70/f1/f14d5b8eeb4b2cd62b9f9f847eb6605f103df89ef619ac68f92f748614ea/pillow-12.1.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d6e4571eedf43af33d0fc233a382a76e849badbccdf1ac438841308652a08e1f", size = 8042721, upload-time = "2026-02-11T04:22:13.321Z" }, + { url = "https://files.pythonhosted.org/packages/5a/d6/17824509146e4babbdabf04d8171491fa9d776f7061ff6e727522df9bd03/pillow-12.1.1-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b574c51cf7d5d62e9be37ba446224b59a2da26dc4c1bb2ecbe936a4fb1a7cb7f", size = 6347798, upload-time = "2026-02-11T04:22:15.449Z" }, + { url = "https://files.pythonhosted.org/packages/d1/ee/c85a38a9ab92037a75615aba572c85ea51e605265036e00c5b67dfafbfe2/pillow-12.1.1-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a37691702ed687799de29a518d63d4682d9016932db66d4e90c345831b02fb4e", size = 7039315, upload-time = "2026-02-11T04:22:17.24Z" }, + { url = "https://files.pythonhosted.org/packages/ec/f3/bc8ccc6e08a148290d7523bde4d9a0d6c981db34631390dc6e6ec34cacf6/pillow-12.1.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:f95c00d5d6700b2b890479664a06e754974848afaae5e21beb4d83c106923fd0", size = 6462360, upload-time = "2026-02-11T04:22:19.111Z" }, + { url = "https://files.pythonhosted.org/packages/f6/ab/69a42656adb1d0665ab051eec58a41f169ad295cf81ad45406963105408f/pillow-12.1.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:559b38da23606e68681337ad74622c4dbba02254fc9cb4488a305dd5975c7eeb", size = 7165438, upload-time = "2026-02-11T04:22:21.041Z" }, + { url = "https://files.pythonhosted.org/packages/02/46/81f7aa8941873f0f01d4b55cc543b0a3d03ec2ee30d617a0448bf6bd6dec/pillow-12.1.1-cp314-cp314-win32.whl", hash = "sha256:03edcc34d688572014ff223c125a3f77fb08091e4607e7745002fc214070b35f", size = 6431503, upload-time = "2026-02-11T04:22:22.833Z" }, + { url = "https://files.pythonhosted.org/packages/40/72/4c245f7d1044b67affc7f134a09ea619d4895333d35322b775b928180044/pillow-12.1.1-cp314-cp314-win_amd64.whl", hash = "sha256:50480dcd74fa63b8e78235957d302d98d98d82ccbfac4c7e12108ba9ecbdba15", size = 7176748, upload-time = "2026-02-11T04:22:24.64Z" }, + { url = "https://files.pythonhosted.org/packages/e4/ad/8a87bdbe038c5c698736e3348af5c2194ffb872ea52f11894c95f9305435/pillow-12.1.1-cp314-cp314-win_arm64.whl", hash = "sha256:5cb1785d97b0c3d1d1a16bc1d710c4a0049daefc4935f3a8f31f827f4d3d2e7f", size = 2544314, upload-time = "2026-02-11T04:22:26.685Z" }, + { url = "https://files.pythonhosted.org/packages/6c/9d/efd18493f9de13b87ede7c47e69184b9e859e4427225ea962e32e56a49bc/pillow-12.1.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1f90cff8aa76835cba5769f0b3121a22bd4eb9e6884cfe338216e557a9a548b8", size = 5268612, upload-time = "2026-02-11T04:22:29.884Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f1/4f42eb2b388eb2ffc660dcb7f7b556c1015c53ebd5f7f754965ef997585b/pillow-12.1.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:1f1be78ce9466a7ee64bfda57bdba0f7cc499d9794d518b854816c41bf0aa4e9", size = 4660567, upload-time = "2026-02-11T04:22:31.799Z" }, + { url = "https://files.pythonhosted.org/packages/01/54/df6ef130fa43e4b82e32624a7b821a2be1c5653a5fdad8469687a7db4e00/pillow-12.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:42fc1f4677106188ad9a55562bbade416f8b55456f522430fadab3cef7cd4e60", size = 6269951, upload-time = "2026-02-11T04:22:33.921Z" }, + { url = "https://files.pythonhosted.org/packages/a9/48/618752d06cc44bb4aae8ce0cd4e6426871929ed7b46215638088270d9b34/pillow-12.1.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:98edb152429ab62a1818039744d8fbb3ccab98a7c29fc3d5fcef158f3f1f68b7", size = 8074769, upload-time = "2026-02-11T04:22:35.877Z" }, + { url = "https://files.pythonhosted.org/packages/c3/bd/f1d71eb39a72fa088d938655afba3e00b38018d052752f435838961127d8/pillow-12.1.1-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d470ab1178551dd17fdba0fef463359c41aaa613cdcd7ff8373f54be629f9f8f", size = 6381358, upload-time = "2026-02-11T04:22:37.698Z" }, + { url = "https://files.pythonhosted.org/packages/64/ef/c784e20b96674ed36a5af839305f55616f8b4f8aa8eeccf8531a6e312243/pillow-12.1.1-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6408a7b064595afcab0a49393a413732a35788f2a5092fdc6266952ed67de586", size = 7068558, upload-time = "2026-02-11T04:22:39.597Z" }, + { url = "https://files.pythonhosted.org/packages/73/cb/8059688b74422ae61278202c4e1ad992e8a2e7375227be0a21c6b87ca8d5/pillow-12.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:5d8c41325b382c07799a3682c1c258469ea2ff97103c53717b7893862d0c98ce", size = 6493028, upload-time = "2026-02-11T04:22:42.73Z" }, + { url = "https://files.pythonhosted.org/packages/c6/da/e3c008ed7d2dd1f905b15949325934510b9d1931e5df999bb15972756818/pillow-12.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c7697918b5be27424e9ce568193efd13d925c4481dd364e43f5dff72d33e10f8", size = 7191940, upload-time = "2026-02-11T04:22:44.543Z" }, + { url = "https://files.pythonhosted.org/packages/01/4a/9202e8d11714c1fc5951f2e1ef362f2d7fbc595e1f6717971d5dd750e969/pillow-12.1.1-cp314-cp314t-win32.whl", hash = "sha256:d2912fd8114fc5545aa3a4b5576512f64c55a03f3ebcca4c10194d593d43ea36", size = 6438736, upload-time = "2026-02-11T04:22:46.347Z" }, + { url = "https://files.pythonhosted.org/packages/f3/ca/cbce2327eb9885476b3957b2e82eb12c866a8b16ad77392864ad601022ce/pillow-12.1.1-cp314-cp314t-win_amd64.whl", hash = "sha256:4ceb838d4bd9dab43e06c363cab2eebf63846d6a4aeaea283bbdfd8f1a8ed58b", size = 7182894, upload-time = "2026-02-11T04:22:48.114Z" }, + { url = "https://files.pythonhosted.org/packages/ec/d2/de599c95ba0a973b94410477f8bf0b6f0b5e67360eb89bcb1ad365258beb/pillow-12.1.1-cp314-cp314t-win_arm64.whl", hash = "sha256:7b03048319bfc6170e93bd60728a1af51d3dd7704935feb228c4d4faab35d334", size = 2546446, upload-time = "2026-02-11T04:22:50.342Z" }, + { url = "https://files.pythonhosted.org/packages/56/11/5d43209aa4cb58e0cc80127956ff1796a68b928e6324bbf06ef4db34367b/pillow-12.1.1-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:600fd103672b925fe62ed08e0d874ea34d692474df6f4bf7ebe148b30f89f39f", size = 5228606, upload-time = "2026-02-11T04:22:52.106Z" }, + { url = "https://files.pythonhosted.org/packages/5f/d5/3b005b4e4fda6698b371fa6c21b097d4707585d7db99e98d9b0b87ac612a/pillow-12.1.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:665e1b916b043cef294bc54d47bf02d87e13f769bc4bc5fa225a24b3a6c5aca9", size = 4622321, upload-time = "2026-02-11T04:22:53.827Z" }, + { url = "https://files.pythonhosted.org/packages/df/36/ed3ea2d594356fd8037e5a01f6156c74bc8d92dbb0fa60746cc96cabb6e8/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:495c302af3aad1ca67420ddd5c7bd480c8867ad173528767d906428057a11f0e", size = 5247579, upload-time = "2026-02-11T04:22:56.094Z" }, + { url = "https://files.pythonhosted.org/packages/54/9a/9cc3e029683cf6d20ae5085da0dafc63148e3252c2f13328e553aaa13cfb/pillow-12.1.1-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8fd420ef0c52c88b5a035a0886f367748c72147b2b8f384c9d12656678dfdfa9", size = 6989094, upload-time = "2026-02-11T04:22:58.288Z" }, + { url = "https://files.pythonhosted.org/packages/00/98/fc53ab36da80b88df0967896b6c4b4cd948a0dc5aa40a754266aa3ae48b3/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f975aa7ef9684ce7e2c18a3aa8f8e2106ce1e46b94ab713d156b2898811651d3", size = 5313850, upload-time = "2026-02-11T04:23:00.554Z" }, + { url = "https://files.pythonhosted.org/packages/30/02/00fa585abfd9fe9d73e5f6e554dc36cc2b842898cbfc46d70353dae227f8/pillow-12.1.1-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8089c852a56c2966cf18835db62d9b34fef7ba74c726ad943928d494fa7f4735", size = 5963343, upload-time = "2026-02-11T04:23:02.934Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/c56ce33ca856e358d27fda9676c055395abddb82c35ac0f593877ed4562e/pillow-12.1.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:cb9bb857b2d057c6dfc72ac5f3b44836924ba15721882ef103cecb40d002d80e", size = 7029880, upload-time = "2026-02-11T04:23:04.783Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.9.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/04/fea538adf7dbbd6d186f551d595961e564a3b6715bdf276b477460858672/platformdirs-4.9.2.tar.gz", hash = "sha256:9a33809944b9db043ad67ca0db94b14bf452cc6aeaac46a88ea55b26e2e9d291", size = 28394, upload-time = "2026-02-16T03:56:10.574Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/48/31/05e764397056194206169869b50cf2fee4dbbbc71b344705b9c0d878d4d8/platformdirs-4.9.2-py3-none-any.whl", hash = "sha256:9170634f126f8efdae22fb58ae8a0eaa86f38365bc57897a6c4f781d1f5875bd", size = 21168, upload-time = "2026-02-16T03:56:08.891Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "prompt-toolkit" +version = "3.0.52" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "wcwidth" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a1/96/06e01a7b38dce6fe1db213e061a4602dd6032a8a97ef6c1a862537732421/prompt_toolkit-3.0.52.tar.gz", hash = "sha256:28cde192929c8e7321de85de1ddbe736f1375148b02f2e17edd840042b1be855", size = 434198, upload-time = "2025-08-27T15:24:02.057Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/84/03/0d3ce49e2505ae70cf43bc5bb3033955d2fc9f932163e84dc0779cc47f48/prompt_toolkit-3.0.52-py3-none-any.whl", hash = "sha256:9aac639a3bbd33284347de5ad8d68ecc044b91a762dc39b7c21095fcd6a19955", size = 391431, upload-time = "2025-08-27T15:23:59.498Z" }, +] + +[[package]] +name = "propcache" +version = "0.4.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9e/da/e9fc233cf63743258bff22b3dfa7ea5baef7b5bc324af47a0ad89b8ffc6f/propcache-0.4.1.tar.gz", hash = "sha256:f48107a8c637e80362555f37ecf49abe20370e557cc4ab374f04ec4423c97c3d", size = 46442, upload-time = "2025-10-08T19:49:02.291Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/0e/934b541323035566a9af292dba85a195f7b78179114f2c6ebb24551118a9/propcache-0.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c2d1fa3201efaf55d730400d945b5b3ab6e672e100ba0f9a409d950ab25d7db", size = 79534, upload-time = "2025-10-08T19:46:02.083Z" }, + { url = "https://files.pythonhosted.org/packages/a1/6b/db0d03d96726d995dc7171286c6ba9d8d14251f37433890f88368951a44e/propcache-0.4.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1eb2994229cc8ce7fe9b3db88f5465f5fd8651672840b2e426b88cdb1a30aac8", size = 45526, upload-time = "2025-10-08T19:46:03.884Z" }, + { url = "https://files.pythonhosted.org/packages/e4/c3/82728404aea669e1600f304f2609cde9e665c18df5a11cdd57ed73c1dceb/propcache-0.4.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:66c1f011f45a3b33d7bcb22daed4b29c0c9e2224758b6be00686731e1b46f925", size = 47263, upload-time = "2025-10-08T19:46:05.405Z" }, + { url = "https://files.pythonhosted.org/packages/df/1b/39313ddad2bf9187a1432654c38249bab4562ef535ef07f5eb6eb04d0b1b/propcache-0.4.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9a52009f2adffe195d0b605c25ec929d26b36ef986ba85244891dee3b294df21", size = 201012, upload-time = "2025-10-08T19:46:07.165Z" }, + { url = "https://files.pythonhosted.org/packages/5b/01/f1d0b57d136f294a142acf97f4ed58c8e5b974c21e543000968357115011/propcache-0.4.1-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5d4e2366a9c7b837555cf02fb9be2e3167d333aff716332ef1b7c3a142ec40c5", size = 209491, upload-time = "2025-10-08T19:46:08.909Z" }, + { url = "https://files.pythonhosted.org/packages/a1/c8/038d909c61c5bb039070b3fb02ad5cccdb1dde0d714792e251cdb17c9c05/propcache-0.4.1-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:9d2b6caef873b4f09e26ea7e33d65f42b944837563a47a94719cc3544319a0db", size = 215319, upload-time = "2025-10-08T19:46:10.7Z" }, + { url = "https://files.pythonhosted.org/packages/08/57/8c87e93142b2c1fa2408e45695205a7ba05fb5db458c0bf5c06ba0e09ea6/propcache-0.4.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2b16ec437a8c8a965ecf95739448dd938b5c7f56e67ea009f4300d8df05f32b7", size = 196856, upload-time = "2025-10-08T19:46:12.003Z" }, + { url = "https://files.pythonhosted.org/packages/42/df/5615fec76aa561987a534759b3686008a288e73107faa49a8ae5795a9f7a/propcache-0.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:296f4c8ed03ca7476813fe666c9ea97869a8d7aec972618671b33a38a5182ef4", size = 193241, upload-time = "2025-10-08T19:46:13.495Z" }, + { url = "https://files.pythonhosted.org/packages/d5/21/62949eb3a7a54afe8327011c90aca7e03547787a88fb8bd9726806482fea/propcache-0.4.1-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:1f0978529a418ebd1f49dad413a2b68af33f85d5c5ca5c6ca2a3bed375a7ac60", size = 190552, upload-time = "2025-10-08T19:46:14.938Z" }, + { url = "https://files.pythonhosted.org/packages/30/ee/ab4d727dd70806e5b4de96a798ae7ac6e4d42516f030ee60522474b6b332/propcache-0.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:fd138803047fb4c062b1c1dd95462f5209456bfab55c734458f15d11da288f8f", size = 200113, upload-time = "2025-10-08T19:46:16.695Z" }, + { url = "https://files.pythonhosted.org/packages/8a/0b/38b46208e6711b016aa8966a3ac793eee0d05c7159d8342aa27fc0bc365e/propcache-0.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:8c9b3cbe4584636d72ff556d9036e0c9317fa27b3ac1f0f558e7e84d1c9c5900", size = 200778, upload-time = "2025-10-08T19:46:18.023Z" }, + { url = "https://files.pythonhosted.org/packages/cf/81/5abec54355ed344476bee711e9f04815d4b00a311ab0535599204eecc257/propcache-0.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:f93243fdc5657247533273ac4f86ae106cc6445a0efacb9a1bfe982fcfefd90c", size = 193047, upload-time = "2025-10-08T19:46:19.449Z" }, + { url = "https://files.pythonhosted.org/packages/ec/b6/1f237c04e32063cb034acd5f6ef34ef3a394f75502e72703545631ab1ef6/propcache-0.4.1-cp310-cp310-win32.whl", hash = "sha256:a0ee98db9c5f80785b266eb805016e36058ac72c51a064040f2bc43b61101cdb", size = 38093, upload-time = "2025-10-08T19:46:20.643Z" }, + { url = "https://files.pythonhosted.org/packages/a6/67/354aac4e0603a15f76439caf0427781bcd6797f370377f75a642133bc954/propcache-0.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:1cdb7988c4e5ac7f6d175a28a9aa0c94cb6f2ebe52756a3c0cda98d2809a9e37", size = 41638, upload-time = "2025-10-08T19:46:21.935Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e1/74e55b9fd1a4c209ff1a9a824bf6c8b3d1fc5a1ac3eabe23462637466785/propcache-0.4.1-cp310-cp310-win_arm64.whl", hash = "sha256:d82ad62b19645419fe79dd63b3f9253e15b30e955c0170e5cebc350c1844e581", size = 38229, upload-time = "2025-10-08T19:46:23.368Z" }, + { url = "https://files.pythonhosted.org/packages/8c/d4/4e2c9aaf7ac2242b9358f98dccd8f90f2605402f5afeff6c578682c2c491/propcache-0.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:60a8fda9644b7dfd5dece8c61d8a85e271cb958075bfc4e01083c148b61a7caf", size = 80208, upload-time = "2025-10-08T19:46:24.597Z" }, + { url = "https://files.pythonhosted.org/packages/c2/21/d7b68e911f9c8e18e4ae43bdbc1e1e9bbd971f8866eb81608947b6f585ff/propcache-0.4.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c30b53e7e6bda1d547cabb47c825f3843a0a1a42b0496087bb58d8fedf9f41b5", size = 45777, upload-time = "2025-10-08T19:46:25.733Z" }, + { url = "https://files.pythonhosted.org/packages/d3/1d/11605e99ac8ea9435651ee71ab4cb4bf03f0949586246476a25aadfec54a/propcache-0.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6918ecbd897443087a3b7cd978d56546a812517dcaaca51b49526720571fa93e", size = 47647, upload-time = "2025-10-08T19:46:27.304Z" }, + { url = "https://files.pythonhosted.org/packages/58/1a/3c62c127a8466c9c843bccb503d40a273e5cc69838805f322e2826509e0d/propcache-0.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d902a36df4e5989763425a8ab9e98cd8ad5c52c823b34ee7ef307fd50582566", size = 214929, upload-time = "2025-10-08T19:46:28.62Z" }, + { url = "https://files.pythonhosted.org/packages/56/b9/8fa98f850960b367c4b8fe0592e7fc341daa7a9462e925228f10a60cf74f/propcache-0.4.1-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a9695397f85973bb40427dedddf70d8dc4a44b22f1650dd4af9eedf443d45165", size = 221778, upload-time = "2025-10-08T19:46:30.358Z" }, + { url = "https://files.pythonhosted.org/packages/46/a6/0ab4f660eb59649d14b3d3d65c439421cf2f87fe5dd68591cbe3c1e78a89/propcache-0.4.1-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:2bb07ffd7eaad486576430c89f9b215f9e4be68c4866a96e97db9e97fead85dc", size = 228144, upload-time = "2025-10-08T19:46:32.607Z" }, + { url = "https://files.pythonhosted.org/packages/52/6a/57f43e054fb3d3a56ac9fc532bc684fc6169a26c75c353e65425b3e56eef/propcache-0.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd6f30fdcf9ae2a70abd34da54f18da086160e4d7d9251f81f3da0ff84fc5a48", size = 210030, upload-time = "2025-10-08T19:46:33.969Z" }, + { url = "https://files.pythonhosted.org/packages/40/e2/27e6feebb5f6b8408fa29f5efbb765cd54c153ac77314d27e457a3e993b7/propcache-0.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:fc38cba02d1acba4e2869eef1a57a43dfbd3d49a59bf90dda7444ec2be6a5570", size = 208252, upload-time = "2025-10-08T19:46:35.309Z" }, + { url = "https://files.pythonhosted.org/packages/9e/f8/91c27b22ccda1dbc7967f921c42825564fa5336a01ecd72eb78a9f4f53c2/propcache-0.4.1-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:67fad6162281e80e882fb3ec355398cf72864a54069d060321f6cd0ade95fe85", size = 202064, upload-time = "2025-10-08T19:46:36.993Z" }, + { url = "https://files.pythonhosted.org/packages/f2/26/7f00bd6bd1adba5aafe5f4a66390f243acab58eab24ff1a08bebb2ef9d40/propcache-0.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:f10207adf04d08bec185bae14d9606a1444715bc99180f9331c9c02093e1959e", size = 212429, upload-time = "2025-10-08T19:46:38.398Z" }, + { url = "https://files.pythonhosted.org/packages/84/89/fd108ba7815c1117ddca79c228f3f8a15fc82a73bca8b142eb5de13b2785/propcache-0.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:e9b0d8d0845bbc4cfcdcbcdbf5086886bc8157aa963c31c777ceff7846c77757", size = 216727, upload-time = "2025-10-08T19:46:39.732Z" }, + { url = "https://files.pythonhosted.org/packages/79/37/3ec3f7e3173e73f1d600495d8b545b53802cbf35506e5732dd8578db3724/propcache-0.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:981333cb2f4c1896a12f4ab92a9cc8f09ea664e9b7dbdc4eff74627af3a11c0f", size = 205097, upload-time = "2025-10-08T19:46:41.025Z" }, + { url = "https://files.pythonhosted.org/packages/61/b0/b2631c19793f869d35f47d5a3a56fb19e9160d3c119f15ac7344fc3ccae7/propcache-0.4.1-cp311-cp311-win32.whl", hash = "sha256:f1d2f90aeec838a52f1c1a32fe9a619fefd5e411721a9117fbf82aea638fe8a1", size = 38084, upload-time = "2025-10-08T19:46:42.693Z" }, + { url = "https://files.pythonhosted.org/packages/f4/78/6cce448e2098e9f3bfc91bb877f06aa24b6ccace872e39c53b2f707c4648/propcache-0.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:364426a62660f3f699949ac8c621aad6977be7126c5807ce48c0aeb8e7333ea6", size = 41637, upload-time = "2025-10-08T19:46:43.778Z" }, + { url = "https://files.pythonhosted.org/packages/9c/e9/754f180cccd7f51a39913782c74717c581b9cc8177ad0e949f4d51812383/propcache-0.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:e53f3a38d3510c11953f3e6a33f205c6d1b001129f972805ca9b42fc308bc239", size = 38064, upload-time = "2025-10-08T19:46:44.872Z" }, + { url = "https://files.pythonhosted.org/packages/a2/0f/f17b1b2b221d5ca28b4b876e8bb046ac40466513960646bda8e1853cdfa2/propcache-0.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e153e9cd40cc8945138822807139367f256f89c6810c2634a4f6902b52d3b4e2", size = 80061, upload-time = "2025-10-08T19:46:46.075Z" }, + { url = "https://files.pythonhosted.org/packages/76/47/8ccf75935f51448ba9a16a71b783eb7ef6b9ee60f5d14c7f8a8a79fbeed7/propcache-0.4.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:cd547953428f7abb73c5ad82cbb32109566204260d98e41e5dfdc682eb7f8403", size = 46037, upload-time = "2025-10-08T19:46:47.23Z" }, + { url = "https://files.pythonhosted.org/packages/0a/b6/5c9a0e42df4d00bfb4a3cbbe5cf9f54260300c88a0e9af1f47ca5ce17ac0/propcache-0.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:f048da1b4f243fc44f205dfd320933a951b8d89e0afd4c7cacc762a8b9165207", size = 47324, upload-time = "2025-10-08T19:46:48.384Z" }, + { url = "https://files.pythonhosted.org/packages/9e/d3/6c7ee328b39a81ee877c962469f1e795f9db87f925251efeb0545e0020d0/propcache-0.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ec17c65562a827bba85e3872ead335f95405ea1674860d96483a02f5c698fa72", size = 225505, upload-time = "2025-10-08T19:46:50.055Z" }, + { url = "https://files.pythonhosted.org/packages/01/5d/1c53f4563490b1d06a684742cc6076ef944bc6457df6051b7d1a877c057b/propcache-0.4.1-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:405aac25c6394ef275dee4c709be43745d36674b223ba4eb7144bf4d691b7367", size = 230242, upload-time = "2025-10-08T19:46:51.815Z" }, + { url = "https://files.pythonhosted.org/packages/20/e1/ce4620633b0e2422207c3cb774a0ee61cac13abc6217763a7b9e2e3f4a12/propcache-0.4.1-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0013cb6f8dde4b2a2f66903b8ba740bdfe378c943c4377a200551ceb27f379e4", size = 238474, upload-time = "2025-10-08T19:46:53.208Z" }, + { url = "https://files.pythonhosted.org/packages/46/4b/3aae6835b8e5f44ea6a68348ad90f78134047b503765087be2f9912140ea/propcache-0.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:15932ab57837c3368b024473a525e25d316d8353016e7cc0e5ba9eb343fbb1cf", size = 221575, upload-time = "2025-10-08T19:46:54.511Z" }, + { url = "https://files.pythonhosted.org/packages/6e/a5/8a5e8678bcc9d3a1a15b9a29165640d64762d424a16af543f00629c87338/propcache-0.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:031dce78b9dc099f4c29785d9cf5577a3faf9ebf74ecbd3c856a7b92768c3df3", size = 216736, upload-time = "2025-10-08T19:46:56.212Z" }, + { url = "https://files.pythonhosted.org/packages/f1/63/b7b215eddeac83ca1c6b934f89d09a625aa9ee4ba158338854c87210cc36/propcache-0.4.1-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ab08df6c9a035bee56e31af99be621526bd237bea9f32def431c656b29e41778", size = 213019, upload-time = "2025-10-08T19:46:57.595Z" }, + { url = "https://files.pythonhosted.org/packages/57/74/f580099a58c8af587cac7ba19ee7cb418506342fbbe2d4a4401661cca886/propcache-0.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4d7af63f9f93fe593afbf104c21b3b15868efb2c21d07d8732c0c4287e66b6a6", size = 220376, upload-time = "2025-10-08T19:46:59.067Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ee/542f1313aff7eaf19c2bb758c5d0560d2683dac001a1c96d0774af799843/propcache-0.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:cfc27c945f422e8b5071b6e93169679e4eb5bf73bbcbf1ba3ae3a83d2f78ebd9", size = 226988, upload-time = "2025-10-08T19:47:00.544Z" }, + { url = "https://files.pythonhosted.org/packages/8f/18/9c6b015dd9c6930f6ce2229e1f02fb35298b847f2087ea2b436a5bfa7287/propcache-0.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:35c3277624a080cc6ec6f847cbbbb5b49affa3598c4535a0a4682a697aaa5c75", size = 215615, upload-time = "2025-10-08T19:47:01.968Z" }, + { url = "https://files.pythonhosted.org/packages/80/9e/e7b85720b98c45a45e1fca6a177024934dc9bc5f4d5dd04207f216fc33ed/propcache-0.4.1-cp312-cp312-win32.whl", hash = "sha256:671538c2262dadb5ba6395e26c1731e1d52534bfe9ae56d0b5573ce539266aa8", size = 38066, upload-time = "2025-10-08T19:47:03.503Z" }, + { url = "https://files.pythonhosted.org/packages/54/09/d19cff2a5aaac632ec8fc03737b223597b1e347416934c1b3a7df079784c/propcache-0.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:cb2d222e72399fcf5890d1d5cc1060857b9b236adff2792ff48ca2dfd46c81db", size = 41655, upload-time = "2025-10-08T19:47:04.973Z" }, + { url = "https://files.pythonhosted.org/packages/68/ab/6b5c191bb5de08036a8c697b265d4ca76148efb10fa162f14af14fb5f076/propcache-0.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:204483131fb222bdaaeeea9f9e6c6ed0cac32731f75dfc1d4a567fc1926477c1", size = 37789, upload-time = "2025-10-08T19:47:06.077Z" }, + { url = "https://files.pythonhosted.org/packages/bf/df/6d9c1b6ac12b003837dde8a10231a7344512186e87b36e855bef32241942/propcache-0.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:43eedf29202c08550aac1d14e0ee619b0430aaef78f85864c1a892294fbc28cf", size = 77750, upload-time = "2025-10-08T19:47:07.648Z" }, + { url = "https://files.pythonhosted.org/packages/8b/e8/677a0025e8a2acf07d3418a2e7ba529c9c33caf09d3c1f25513023c1db56/propcache-0.4.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:d62cdfcfd89ccb8de04e0eda998535c406bf5e060ffd56be6c586cbcc05b3311", size = 44780, upload-time = "2025-10-08T19:47:08.851Z" }, + { url = "https://files.pythonhosted.org/packages/89/a4/92380f7ca60f99ebae761936bc48a72a639e8a47b29050615eef757cb2a7/propcache-0.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:cae65ad55793da34db5f54e4029b89d3b9b9490d8abe1b4c7ab5d4b8ec7ebf74", size = 46308, upload-time = "2025-10-08T19:47:09.982Z" }, + { url = "https://files.pythonhosted.org/packages/2d/48/c5ac64dee5262044348d1d78a5f85dd1a57464a60d30daee946699963eb3/propcache-0.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:333ddb9031d2704a301ee3e506dc46b1fe5f294ec198ed6435ad5b6a085facfe", size = 208182, upload-time = "2025-10-08T19:47:11.319Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0c/cd762dd011a9287389a6a3eb43aa30207bde253610cca06824aeabfe9653/propcache-0.4.1-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fd0858c20f078a32cf55f7e81473d96dcf3b93fd2ccdb3d40fdf54b8573df3af", size = 211215, upload-time = "2025-10-08T19:47:13.146Z" }, + { url = "https://files.pythonhosted.org/packages/30/3e/49861e90233ba36890ae0ca4c660e95df565b2cd15d4a68556ab5865974e/propcache-0.4.1-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:678ae89ebc632c5c204c794f8dab2837c5f159aeb59e6ed0539500400577298c", size = 218112, upload-time = "2025-10-08T19:47:14.913Z" }, + { url = "https://files.pythonhosted.org/packages/f1/8b/544bc867e24e1bd48f3118cecd3b05c694e160a168478fa28770f22fd094/propcache-0.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d472aeb4fbf9865e0c6d622d7f4d54a4e101a89715d8904282bb5f9a2f476c3f", size = 204442, upload-time = "2025-10-08T19:47:16.277Z" }, + { url = "https://files.pythonhosted.org/packages/50/a6/4282772fd016a76d3e5c0df58380a5ea64900afd836cec2c2f662d1b9bb3/propcache-0.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:4d3df5fa7e36b3225954fba85589da77a0fe6a53e3976de39caf04a0db4c36f1", size = 199398, upload-time = "2025-10-08T19:47:17.962Z" }, + { url = "https://files.pythonhosted.org/packages/3e/ec/d8a7cd406ee1ddb705db2139f8a10a8a427100347bd698e7014351c7af09/propcache-0.4.1-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:ee17f18d2498f2673e432faaa71698032b0127ebf23ae5974eeaf806c279df24", size = 196920, upload-time = "2025-10-08T19:47:19.355Z" }, + { url = "https://files.pythonhosted.org/packages/f6/6c/f38ab64af3764f431e359f8baf9e0a21013e24329e8b85d2da32e8ed07ca/propcache-0.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:580e97762b950f993ae618e167e7be9256b8353c2dcd8b99ec100eb50f5286aa", size = 203748, upload-time = "2025-10-08T19:47:21.338Z" }, + { url = "https://files.pythonhosted.org/packages/d6/e3/fa846bd70f6534d647886621388f0a265254d30e3ce47e5c8e6e27dbf153/propcache-0.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:501d20b891688eb8e7aa903021f0b72d5a55db40ffaab27edefd1027caaafa61", size = 205877, upload-time = "2025-10-08T19:47:23.059Z" }, + { url = "https://files.pythonhosted.org/packages/e2/39/8163fc6f3133fea7b5f2827e8eba2029a0277ab2c5beee6c1db7b10fc23d/propcache-0.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:9a0bd56e5b100aef69bd8562b74b46254e7c8812918d3baa700c8a8009b0af66", size = 199437, upload-time = "2025-10-08T19:47:24.445Z" }, + { url = "https://files.pythonhosted.org/packages/93/89/caa9089970ca49c7c01662bd0eeedfe85494e863e8043565aeb6472ce8fe/propcache-0.4.1-cp313-cp313-win32.whl", hash = "sha256:bcc9aaa5d80322bc2fb24bb7accb4a30f81e90ab8d6ba187aec0744bc302ad81", size = 37586, upload-time = "2025-10-08T19:47:25.736Z" }, + { url = "https://files.pythonhosted.org/packages/f5/ab/f76ec3c3627c883215b5c8080debb4394ef5a7a29be811f786415fc1e6fd/propcache-0.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:381914df18634f5494334d201e98245c0596067504b9372d8cf93f4bb23e025e", size = 40790, upload-time = "2025-10-08T19:47:26.847Z" }, + { url = "https://files.pythonhosted.org/packages/59/1b/e71ae98235f8e2ba5004d8cb19765a74877abf189bc53fc0c80d799e56c3/propcache-0.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:8873eb4460fd55333ea49b7d189749ecf6e55bf85080f11b1c4530ed3034cba1", size = 37158, upload-time = "2025-10-08T19:47:27.961Z" }, + { url = "https://files.pythonhosted.org/packages/83/ce/a31bbdfc24ee0dcbba458c8175ed26089cf109a55bbe7b7640ed2470cfe9/propcache-0.4.1-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:92d1935ee1f8d7442da9c0c4fa7ac20d07e94064184811b685f5c4fada64553b", size = 81451, upload-time = "2025-10-08T19:47:29.445Z" }, + { url = "https://files.pythonhosted.org/packages/25/9c/442a45a470a68456e710d96cacd3573ef26a1d0a60067e6a7d5e655621ed/propcache-0.4.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:473c61b39e1460d386479b9b2f337da492042447c9b685f28be4f74d3529e566", size = 46374, upload-time = "2025-10-08T19:47:30.579Z" }, + { url = "https://files.pythonhosted.org/packages/f4/bf/b1d5e21dbc3b2e889ea4327044fb16312a736d97640fb8b6aa3f9c7b3b65/propcache-0.4.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:c0ef0aaafc66fbd87842a3fe3902fd889825646bc21149eafe47be6072725835", size = 48396, upload-time = "2025-10-08T19:47:31.79Z" }, + { url = "https://files.pythonhosted.org/packages/f4/04/5b4c54a103d480e978d3c8a76073502b18db0c4bc17ab91b3cb5092ad949/propcache-0.4.1-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f95393b4d66bfae908c3ca8d169d5f79cd65636ae15b5e7a4f6e67af675adb0e", size = 275950, upload-time = "2025-10-08T19:47:33.481Z" }, + { url = "https://files.pythonhosted.org/packages/b4/c1/86f846827fb969c4b78b0af79bba1d1ea2156492e1b83dea8b8a6ae27395/propcache-0.4.1-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c07fda85708bc48578467e85099645167a955ba093be0a2dcba962195676e859", size = 273856, upload-time = "2025-10-08T19:47:34.906Z" }, + { url = "https://files.pythonhosted.org/packages/36/1d/fc272a63c8d3bbad6878c336c7a7dea15e8f2d23a544bda43205dfa83ada/propcache-0.4.1-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:af223b406d6d000830c6f65f1e6431783fc3f713ba3e6cc8c024d5ee96170a4b", size = 280420, upload-time = "2025-10-08T19:47:36.338Z" }, + { url = "https://files.pythonhosted.org/packages/07/0c/01f2219d39f7e53d52e5173bcb09c976609ba30209912a0680adfb8c593a/propcache-0.4.1-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a78372c932c90ee474559c5ddfffd718238e8673c340dc21fe45c5b8b54559a0", size = 263254, upload-time = "2025-10-08T19:47:37.692Z" }, + { url = "https://files.pythonhosted.org/packages/2d/18/cd28081658ce597898f0c4d174d4d0f3c5b6d4dc27ffafeef835c95eb359/propcache-0.4.1-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:564d9f0d4d9509e1a870c920a89b2fec951b44bf5ba7d537a9e7c1ccec2c18af", size = 261205, upload-time = "2025-10-08T19:47:39.659Z" }, + { url = "https://files.pythonhosted.org/packages/7a/71/1f9e22eb8b8316701c2a19fa1f388c8a3185082607da8e406a803c9b954e/propcache-0.4.1-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:17612831fda0138059cc5546f4d12a2aacfb9e47068c06af35c400ba58ba7393", size = 247873, upload-time = "2025-10-08T19:47:41.084Z" }, + { url = "https://files.pythonhosted.org/packages/4a/65/3d4b61f36af2b4eddba9def857959f1016a51066b4f1ce348e0cf7881f58/propcache-0.4.1-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:41a89040cb10bd345b3c1a873b2bf36413d48da1def52f268a055f7398514874", size = 262739, upload-time = "2025-10-08T19:47:42.51Z" }, + { url = "https://files.pythonhosted.org/packages/2a/42/26746ab087faa77c1c68079b228810436ccd9a5ce9ac85e2b7307195fd06/propcache-0.4.1-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:e35b88984e7fa64aacecea39236cee32dd9bd8c55f57ba8a75cf2399553f9bd7", size = 263514, upload-time = "2025-10-08T19:47:43.927Z" }, + { url = "https://files.pythonhosted.org/packages/94/13/630690fe201f5502d2403dd3cfd451ed8858fe3c738ee88d095ad2ff407b/propcache-0.4.1-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:6f8b465489f927b0df505cbe26ffbeed4d6d8a2bbc61ce90eb074ff129ef0ab1", size = 257781, upload-time = "2025-10-08T19:47:45.448Z" }, + { url = "https://files.pythonhosted.org/packages/92/f7/1d4ec5841505f423469efbfc381d64b7b467438cd5a4bbcbb063f3b73d27/propcache-0.4.1-cp313-cp313t-win32.whl", hash = "sha256:2ad890caa1d928c7c2965b48f3a3815c853180831d0e5503d35cf00c472f4717", size = 41396, upload-time = "2025-10-08T19:47:47.202Z" }, + { url = "https://files.pythonhosted.org/packages/48/f0/615c30622316496d2cbbc29f5985f7777d3ada70f23370608c1d3e081c1f/propcache-0.4.1-cp313-cp313t-win_amd64.whl", hash = "sha256:f7ee0e597f495cf415bcbd3da3caa3bd7e816b74d0d52b8145954c5e6fd3ff37", size = 44897, upload-time = "2025-10-08T19:47:48.336Z" }, + { url = "https://files.pythonhosted.org/packages/fd/ca/6002e46eccbe0e33dcd4069ef32f7f1c9e243736e07adca37ae8c4830ec3/propcache-0.4.1-cp313-cp313t-win_arm64.whl", hash = "sha256:929d7cbe1f01bb7baffb33dc14eb5691c95831450a26354cd210a8155170c93a", size = 39789, upload-time = "2025-10-08T19:47:49.876Z" }, + { url = "https://files.pythonhosted.org/packages/8e/5c/bca52d654a896f831b8256683457ceddd490ec18d9ec50e97dfd8fc726a8/propcache-0.4.1-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:3f7124c9d820ba5548d431afb4632301acf965db49e666aa21c305cbe8c6de12", size = 78152, upload-time = "2025-10-08T19:47:51.051Z" }, + { url = "https://files.pythonhosted.org/packages/65/9b/03b04e7d82a5f54fb16113d839f5ea1ede58a61e90edf515f6577c66fa8f/propcache-0.4.1-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:c0d4b719b7da33599dfe3b22d3db1ef789210a0597bc650b7cee9c77c2be8c5c", size = 44869, upload-time = "2025-10-08T19:47:52.594Z" }, + { url = "https://files.pythonhosted.org/packages/b2/fa/89a8ef0468d5833a23fff277b143d0573897cf75bd56670a6d28126c7d68/propcache-0.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f302f4783709a78240ebc311b793f123328716a60911d667e0c036bc5dcbded", size = 46596, upload-time = "2025-10-08T19:47:54.073Z" }, + { url = "https://files.pythonhosted.org/packages/86/bd/47816020d337f4a746edc42fe8d53669965138f39ee117414c7d7a340cfe/propcache-0.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c80ee5802e3fb9ea37938e7eecc307fb984837091d5fd262bb37238b1ae97641", size = 206981, upload-time = "2025-10-08T19:47:55.715Z" }, + { url = "https://files.pythonhosted.org/packages/df/f6/c5fa1357cc9748510ee55f37173eb31bfde6d94e98ccd9e6f033f2fc06e1/propcache-0.4.1-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ed5a841e8bb29a55fb8159ed526b26adc5bdd7e8bd7bf793ce647cb08656cdf4", size = 211490, upload-time = "2025-10-08T19:47:57.499Z" }, + { url = "https://files.pythonhosted.org/packages/80/1e/e5889652a7c4a3846683401a48f0f2e5083ce0ec1a8a5221d8058fbd1adf/propcache-0.4.1-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:55c72fd6ea2da4c318e74ffdf93c4fe4e926051133657459131a95c846d16d44", size = 215371, upload-time = "2025-10-08T19:47:59.317Z" }, + { url = "https://files.pythonhosted.org/packages/b2/f2/889ad4b2408f72fe1a4f6a19491177b30ea7bf1a0fd5f17050ca08cfc882/propcache-0.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8326e144341460402713f91df60ade3c999d601e7eb5ff8f6f7862d54de0610d", size = 201424, upload-time = "2025-10-08T19:48:00.67Z" }, + { url = "https://files.pythonhosted.org/packages/27/73/033d63069b57b0812c8bd19f311faebeceb6ba31b8f32b73432d12a0b826/propcache-0.4.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:060b16ae65bc098da7f6d25bf359f1f31f688384858204fe5d652979e0015e5b", size = 197566, upload-time = "2025-10-08T19:48:02.604Z" }, + { url = "https://files.pythonhosted.org/packages/dc/89/ce24f3dc182630b4e07aa6d15f0ff4b14ed4b9955fae95a0b54c58d66c05/propcache-0.4.1-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:89eb3fa9524f7bec9de6e83cf3faed9d79bffa560672c118a96a171a6f55831e", size = 193130, upload-time = "2025-10-08T19:48:04.499Z" }, + { url = "https://files.pythonhosted.org/packages/a9/24/ef0d5fd1a811fb5c609278d0209c9f10c35f20581fcc16f818da959fc5b4/propcache-0.4.1-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:dee69d7015dc235f526fe80a9c90d65eb0039103fe565776250881731f06349f", size = 202625, upload-time = "2025-10-08T19:48:06.213Z" }, + { url = "https://files.pythonhosted.org/packages/f5/02/98ec20ff5546f68d673df2f7a69e8c0d076b5abd05ca882dc7ee3a83653d/propcache-0.4.1-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:5558992a00dfd54ccbc64a32726a3357ec93825a418a401f5cc67df0ac5d9e49", size = 204209, upload-time = "2025-10-08T19:48:08.432Z" }, + { url = "https://files.pythonhosted.org/packages/a0/87/492694f76759b15f0467a2a93ab68d32859672b646aa8a04ce4864e7932d/propcache-0.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:c9b822a577f560fbd9554812526831712c1436d2c046cedee4c3796d3543b144", size = 197797, upload-time = "2025-10-08T19:48:09.968Z" }, + { url = "https://files.pythonhosted.org/packages/ee/36/66367de3575db1d2d3f3d177432bd14ee577a39d3f5d1b3d5df8afe3b6e2/propcache-0.4.1-cp314-cp314-win32.whl", hash = "sha256:ab4c29b49d560fe48b696cdcb127dd36e0bc2472548f3bf56cc5cb3da2b2984f", size = 38140, upload-time = "2025-10-08T19:48:11.232Z" }, + { url = "https://files.pythonhosted.org/packages/0c/2a/a758b47de253636e1b8aef181c0b4f4f204bf0dd964914fb2af90a95b49b/propcache-0.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:5a103c3eb905fcea0ab98be99c3a9a5ab2de60228aa5aceedc614c0281cf6153", size = 41257, upload-time = "2025-10-08T19:48:12.707Z" }, + { url = "https://files.pythonhosted.org/packages/34/5e/63bd5896c3fec12edcbd6f12508d4890d23c265df28c74b175e1ef9f4f3b/propcache-0.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:74c1fb26515153e482e00177a1ad654721bf9207da8a494a0c05e797ad27b992", size = 38097, upload-time = "2025-10-08T19:48:13.923Z" }, + { url = "https://files.pythonhosted.org/packages/99/85/9ff785d787ccf9bbb3f3106f79884a130951436f58392000231b4c737c80/propcache-0.4.1-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:824e908bce90fb2743bd6b59db36eb4f45cd350a39637c9f73b1c1ea66f5b75f", size = 81455, upload-time = "2025-10-08T19:48:15.16Z" }, + { url = "https://files.pythonhosted.org/packages/90/85/2431c10c8e7ddb1445c1f7c4b54d886e8ad20e3c6307e7218f05922cad67/propcache-0.4.1-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:c2b5e7db5328427c57c8e8831abda175421b709672f6cfc3d630c3b7e2146393", size = 46372, upload-time = "2025-10-08T19:48:16.424Z" }, + { url = "https://files.pythonhosted.org/packages/01/20/b0972d902472da9bcb683fa595099911f4d2e86e5683bcc45de60dd05dc3/propcache-0.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:6f6ff873ed40292cd4969ef5310179afd5db59fdf055897e282485043fc80ad0", size = 48411, upload-time = "2025-10-08T19:48:17.577Z" }, + { url = "https://files.pythonhosted.org/packages/e2/e3/7dc89f4f21e8f99bad3d5ddb3a3389afcf9da4ac69e3deb2dcdc96e74169/propcache-0.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:49a2dc67c154db2c1463013594c458881a069fcf98940e61a0569016a583020a", size = 275712, upload-time = "2025-10-08T19:48:18.901Z" }, + { url = "https://files.pythonhosted.org/packages/20/67/89800c8352489b21a8047c773067644e3897f02ecbbd610f4d46b7f08612/propcache-0.4.1-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:005f08e6a0529984491e37d8dbc3dd86f84bd78a8ceb5fa9a021f4c48d4984be", size = 273557, upload-time = "2025-10-08T19:48:20.762Z" }, + { url = "https://files.pythonhosted.org/packages/e2/a1/b52b055c766a54ce6d9c16d9aca0cad8059acd9637cdf8aa0222f4a026ef/propcache-0.4.1-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:5c3310452e0d31390da9035c348633b43d7e7feb2e37be252be6da45abd1abcc", size = 280015, upload-time = "2025-10-08T19:48:22.592Z" }, + { url = "https://files.pythonhosted.org/packages/48/c8/33cee30bd890672c63743049f3c9e4be087e6780906bfc3ec58528be59c1/propcache-0.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c3c70630930447f9ef1caac7728c8ad1c56bc5015338b20fed0d08ea2480b3a", size = 262880, upload-time = "2025-10-08T19:48:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/0c/b1/8f08a143b204b418285c88b83d00edbd61afbc2c6415ffafc8905da7038b/propcache-0.4.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e57061305815dfc910a3634dcf584f08168a8836e6999983569f51a8544cd89", size = 260938, upload-time = "2025-10-08T19:48:25.656Z" }, + { url = "https://files.pythonhosted.org/packages/cf/12/96e4664c82ca2f31e1c8dff86afb867348979eb78d3cb8546a680287a1e9/propcache-0.4.1-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:521a463429ef54143092c11a77e04056dd00636f72e8c45b70aaa3140d639726", size = 247641, upload-time = "2025-10-08T19:48:27.207Z" }, + { url = "https://files.pythonhosted.org/packages/18/ed/e7a9cfca28133386ba52278136d42209d3125db08d0a6395f0cba0c0285c/propcache-0.4.1-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:120c964da3fdc75e3731aa392527136d4ad35868cc556fd09bb6d09172d9a367", size = 262510, upload-time = "2025-10-08T19:48:28.65Z" }, + { url = "https://files.pythonhosted.org/packages/f5/76/16d8bf65e8845dd62b4e2b57444ab81f07f40caa5652b8969b87ddcf2ef6/propcache-0.4.1-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:d8f353eb14ee3441ee844ade4277d560cdd68288838673273b978e3d6d2c8f36", size = 263161, upload-time = "2025-10-08T19:48:30.133Z" }, + { url = "https://files.pythonhosted.org/packages/e7/70/c99e9edb5d91d5ad8a49fa3c1e8285ba64f1476782fed10ab251ff413ba1/propcache-0.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ab2943be7c652f09638800905ee1bab2c544e537edb57d527997a24c13dc1455", size = 257393, upload-time = "2025-10-08T19:48:31.567Z" }, + { url = "https://files.pythonhosted.org/packages/08/02/87b25304249a35c0915d236575bc3574a323f60b47939a2262b77632a3ee/propcache-0.4.1-cp314-cp314t-win32.whl", hash = "sha256:05674a162469f31358c30bcaa8883cb7829fa3110bf9c0991fe27d7896c42d85", size = 42546, upload-time = "2025-10-08T19:48:32.872Z" }, + { url = "https://files.pythonhosted.org/packages/cb/ef/3c6ecf8b317aa982f309835e8f96987466123c6e596646d4e6a1dfcd080f/propcache-0.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:990f6b3e2a27d683cb7602ed6c86f15ee6b43b1194736f9baaeb93d0016633b1", size = 46259, upload-time = "2025-10-08T19:48:34.226Z" }, + { url = "https://files.pythonhosted.org/packages/c4/2d/346e946d4951f37eca1e4f55be0f0174c52cd70720f84029b02f296f4a38/propcache-0.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ecef2343af4cc68e05131e45024ba34f6095821988a9d0a02aa7c73fcc448aa9", size = 40428, upload-time = "2025-10-08T19:48:35.441Z" }, + { url = "https://files.pythonhosted.org/packages/9b/01/0ebaec9003f5d619a7475165961f8e3083cf8644d704b60395df3601632d/propcache-0.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3d233076ccf9e450c8b3bc6720af226b898ef5d051a2d145f7d765e6e9f9bcff", size = 80277, upload-time = "2025-10-08T19:48:36.647Z" }, + { url = "https://files.pythonhosted.org/packages/34/58/04af97ac586b4ef6b9026c3fd36ee7798b737a832f5d3440a4280dcebd3a/propcache-0.4.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:357f5bb5c377a82e105e44bd3d52ba22b616f7b9773714bff93573988ef0a5fb", size = 45865, upload-time = "2025-10-08T19:48:37.859Z" }, + { url = "https://files.pythonhosted.org/packages/7c/19/b65d98ae21384518b291d9939e24a8aeac4fdb5101b732576f8f7540e834/propcache-0.4.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:cbc3b6dfc728105b2a57c06791eb07a94229202ea75c59db644d7d496b698cac", size = 47636, upload-time = "2025-10-08T19:48:39.038Z" }, + { url = "https://files.pythonhosted.org/packages/b3/0f/317048c6d91c356c7154dca5af019e6effeb7ee15fa6a6db327cc19e12b4/propcache-0.4.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:182b51b421f0501952d938dc0b0eb45246a5b5153c50d42b495ad5fb7517c888", size = 201126, upload-time = "2025-10-08T19:48:40.774Z" }, + { url = "https://files.pythonhosted.org/packages/71/69/0b2a7a5a6ee83292b4b997dbd80549d8ce7d40b6397c1646c0d9495f5a85/propcache-0.4.1-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:4b536b39c5199b96fc6245eb5fb796c497381d3942f169e44e8e392b29c9ebcc", size = 209837, upload-time = "2025-10-08T19:48:42.167Z" }, + { url = "https://files.pythonhosted.org/packages/a5/92/c699ac495a6698df6e497fc2de27af4b6ace10d8e76528357ce153722e45/propcache-0.4.1-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:db65d2af507bbfbdcedb254a11149f894169d90488dd3e7190f7cdcb2d6cd57a", size = 215578, upload-time = "2025-10-08T19:48:43.56Z" }, + { url = "https://files.pythonhosted.org/packages/b3/ee/14de81c5eb02c0ee4f500b4e39c4e1bd0677c06e72379e6ab18923c773fc/propcache-0.4.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd2dbc472da1f772a4dae4fa24be938a6c544671a912e30529984dd80400cd88", size = 197187, upload-time = "2025-10-08T19:48:45.309Z" }, + { url = "https://files.pythonhosted.org/packages/1d/94/48dce9aaa6d8dd5a0859bad75158ec522546d4ac23f8e2f05fac469477dd/propcache-0.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:daede9cd44e0f8bdd9e6cc9a607fc81feb80fae7a5fc6cecaff0e0bb32e42d00", size = 193478, upload-time = "2025-10-08T19:48:47.743Z" }, + { url = "https://files.pythonhosted.org/packages/60/b5/0516b563e801e1ace212afde869a0596a0d7115eec0b12d296d75633fb29/propcache-0.4.1-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:71b749281b816793678ae7f3d0d84bd36e694953822eaad408d682efc5ca18e0", size = 190650, upload-time = "2025-10-08T19:48:49.373Z" }, + { url = "https://files.pythonhosted.org/packages/24/89/e0f7d4a5978cd56f8cd67735f74052f257dc471ec901694e430f0d1572fe/propcache-0.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:0002004213ee1f36cfb3f9a42b5066100c44276b9b72b4e1504cddd3d692e86e", size = 200251, upload-time = "2025-10-08T19:48:51.4Z" }, + { url = "https://files.pythonhosted.org/packages/06/7d/a1fac863d473876ed4406c914f2e14aa82d2f10dd207c9e16fc383cc5a24/propcache-0.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:fe49d0a85038f36ba9e3ffafa1103e61170b28e95b16622e11be0a0ea07c6781", size = 200919, upload-time = "2025-10-08T19:48:53.227Z" }, + { url = "https://files.pythonhosted.org/packages/c3/4e/f86a256ff24944cf5743e4e6c6994e3526f6acfcfb55e21694c2424f758c/propcache-0.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:99d43339c83aaf4d32bda60928231848eee470c6bda8d02599cc4cebe872d183", size = 193211, upload-time = "2025-10-08T19:48:55.027Z" }, + { url = "https://files.pythonhosted.org/packages/6e/3f/3fbad5f4356b068f1b047d300a6ff2c66614d7030f078cd50be3fec04228/propcache-0.4.1-cp39-cp39-win32.whl", hash = "sha256:a129e76735bc792794d5177069691c3217898b9f5cee2b2661471e52ffe13f19", size = 38314, upload-time = "2025-10-08T19:48:56.792Z" }, + { url = "https://files.pythonhosted.org/packages/a4/45/d78d136c3a3d215677abb886785aae744da2c3005bcb99e58640c56529b1/propcache-0.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:948dab269721ae9a87fd16c514a0a2c2a1bdb23a9a61b969b0f9d9ee2968546f", size = 41912, upload-time = "2025-10-08T19:48:57.995Z" }, + { url = "https://files.pythonhosted.org/packages/fc/2a/b0632941f25139f4e58450b307242951f7c2717a5704977c6d5323a800af/propcache-0.4.1-cp39-cp39-win_arm64.whl", hash = "sha256:5fd37c406dd6dc85aa743e214cef35dc54bbdd1419baac4f6ae5e5b1a2976938", size = 38450, upload-time = "2025-10-08T19:48:59.349Z" }, + { url = "https://files.pythonhosted.org/packages/5b/5a/bc7b4a4ef808fa59a816c17b20c4bef6884daebbdf627ff2a161da67da19/propcache-0.4.1-py3-none-any.whl", hash = "sha256:af2a6052aeb6cf17d3e46ee169099044fd8224cbaf75c76a2ef596e8163e2237", size = 13305, upload-time = "2025-10-08T19:49:00.792Z" }, +] + +[[package]] +name = "protobuf" +version = "6.33.5" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/ba/25/7c72c307aafc96fa87062aa6291d9f7c94836e43214d43722e86037aac02/protobuf-6.33.5.tar.gz", hash = "sha256:6ddcac2a081f8b7b9642c09406bc6a4290128fce5f471cddd165960bb9119e5c", size = 444465, upload-time = "2026-01-29T21:51:33.494Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b1/79/af92d0a8369732b027e6d6084251dd8e782c685c72da161bd4a2e00fbabb/protobuf-6.33.5-cp310-abi3-win32.whl", hash = "sha256:d71b040839446bac0f4d162e758bea99c8251161dae9d0983a3b88dee345153b", size = 425769, upload-time = "2026-01-29T21:51:21.751Z" }, + { url = "https://files.pythonhosted.org/packages/55/75/bb9bc917d10e9ee13dee8607eb9ab963b7cf8be607c46e7862c748aa2af7/protobuf-6.33.5-cp310-abi3-win_amd64.whl", hash = "sha256:3093804752167bcab3998bec9f1048baae6e29505adaf1afd14a37bddede533c", size = 437118, upload-time = "2026-01-29T21:51:24.022Z" }, + { url = "https://files.pythonhosted.org/packages/a2/6b/e48dfc1191bc5b52950246275bf4089773e91cb5ba3592621723cdddca62/protobuf-6.33.5-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:a5cb85982d95d906df1e2210e58f8e4f1e3cdc088e52c921a041f9c9a0386de5", size = 427766, upload-time = "2026-01-29T21:51:25.413Z" }, + { url = "https://files.pythonhosted.org/packages/4e/b1/c79468184310de09d75095ed1314b839eb2f72df71097db9d1404a1b2717/protobuf-6.33.5-cp39-abi3-manylinux2014_aarch64.whl", hash = "sha256:9b71e0281f36f179d00cbcb119cb19dec4d14a81393e5ea220f64b286173e190", size = 324638, upload-time = "2026-01-29T21:51:26.423Z" }, + { url = "https://files.pythonhosted.org/packages/c5/f5/65d838092fd01c44d16037953fd4c2cc851e783de9b8f02b27ec4ffd906f/protobuf-6.33.5-cp39-abi3-manylinux2014_s390x.whl", hash = "sha256:8afa18e1d6d20af15b417e728e9f60f3aa108ee76f23c3b2c07a2c3b546d3afd", size = 339411, upload-time = "2026-01-29T21:51:27.446Z" }, + { url = "https://files.pythonhosted.org/packages/9b/53/a9443aa3ca9ba8724fdfa02dd1887c1bcd8e89556b715cfbacca6b63dbec/protobuf-6.33.5-cp39-abi3-manylinux2014_x86_64.whl", hash = "sha256:cbf16ba3350fb7b889fca858fb215967792dc125b35c7976ca4818bee3521cf0", size = 323465, upload-time = "2026-01-29T21:51:28.925Z" }, + { url = "https://files.pythonhosted.org/packages/08/60/84d5f6dcda9165e4d6a56ac8433c9f40a8906bf2966150b8a0cfde097d78/protobuf-6.33.5-cp39-cp39-win32.whl", hash = "sha256:a3157e62729aafb8df6da2c03aa5c0937c7266c626ce11a278b6eb7963c4e37c", size = 425892, upload-time = "2026-01-29T21:51:30.382Z" }, + { url = "https://files.pythonhosted.org/packages/68/19/33d7dc2dc84439587fa1e21e1c0026c01ad2af0a62f58fd54002a7546307/protobuf-6.33.5-cp39-cp39-win_amd64.whl", hash = "sha256:8f04fa32763dcdb4973d537d6b54e615cc61108c7cb38fe59310c3192d29510a", size = 437137, upload-time = "2026-01-29T21:51:31.456Z" }, + { url = "https://files.pythonhosted.org/packages/57/bf/2086963c69bdac3d7cff1cc7ff79b8ce5ea0bec6797a017e1be338a46248/protobuf-6.33.5-py3-none-any.whl", hash = "sha256:69915a973dd0f60f31a08b8318b73eab2bd6a392c79184b3612226b0a3f8ec02", size = 170687, upload-time = "2026-01-29T21:51:32.557Z" }, +] + +[[package]] +name = "protobuf" +version = "7.34.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/f2/00/04a2ab36b70a52d0356852979e08b44edde0435f2115dc66e25f2100f3ab/protobuf-7.34.0.tar.gz", hash = "sha256:3871a3df67c710aaf7bb8d214cc997342e63ceebd940c8c7fc65c9b3d697591a", size = 454726, upload-time = "2026-02-27T00:30:25.421Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/c4/6322ab5c8f279c4c358bc14eb8aefc0550b97222a39f04eb3c1af7a830fa/protobuf-7.34.0-cp310-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e329966799f2c271d5e05e236459fe1cbfdb8755aaa3b0914fa60947ddea408", size = 429248, upload-time = "2026-02-27T00:30:14.924Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/b029bbbc61e8937545da5b79aa405ab2d9cf307a728f8c9459ad60d7a481/protobuf-7.34.0-cp310-abi3-manylinux2014_aarch64.whl", hash = "sha256:9d7a5005fb96f3c1e64f397f91500b0eb371b28da81296ae73a6b08a5b76cdd6", size = 325753, upload-time = "2026-02-27T00:30:17.247Z" }, + { url = "https://files.pythonhosted.org/packages/cc/79/09f02671eb75b251c5550a1c48e7b3d4b0623efd7c95a15a50f6f9fc1e2e/protobuf-7.34.0-cp310-abi3-manylinux2014_s390x.whl", hash = "sha256:4a72a8ec94e7a9f7ef7fe818ed26d073305f347f8b3b5ba31e22f81fd85fca02", size = 340200, upload-time = "2026-02-27T00:30:18.672Z" }, + { url = "https://files.pythonhosted.org/packages/b5/57/89727baef7578897af5ed166735ceb315819f1c184da8c3441271dbcfde7/protobuf-7.34.0-cp310-abi3-manylinux2014_x86_64.whl", hash = "sha256:964cf977e07f479c0697964e83deda72bcbc75c3badab506fb061b352d991b01", size = 324268, upload-time = "2026-02-27T00:30:20.088Z" }, + { url = "https://files.pythonhosted.org/packages/1f/3e/38ff2ddee5cc946f575c9d8cc822e34bde205cf61acf8099ad88ef19d7d2/protobuf-7.34.0-cp310-abi3-win32.whl", hash = "sha256:f791ec509707a1d91bd02e07df157e75e4fb9fbdad12a81b7396201ec244e2e3", size = 426628, upload-time = "2026-02-27T00:30:21.555Z" }, + { url = "https://files.pythonhosted.org/packages/cb/71/7c32eaf34a61a1bae1b62a2ac4ffe09b8d1bb0cf93ad505f42040023db89/protobuf-7.34.0-cp310-abi3-win_amd64.whl", hash = "sha256:9f9079f1dde4e32342ecbd1c118d76367090d4aaa19da78230c38101c5b3dd40", size = 437901, upload-time = "2026-02-27T00:30:22.836Z" }, + { url = "https://files.pythonhosted.org/packages/a4/e7/14dc9366696dcb53a413449881743426ed289d687bcf3d5aee4726c32ebb/protobuf-7.34.0-py3-none-any.whl", hash = "sha256:e3b914dd77fa33fa06ab2baa97937746ab25695f389869afdf03e81f34e45dc7", size = 170716, upload-time = "2026-02-27T00:30:23.994Z" }, +] + +[[package]] +name = "ptyprocess" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/20/e5/16ff212c1e452235a90aeb09066144d0c5a6a8c0834397e03f5224495c4e/ptyprocess-0.7.0.tar.gz", hash = "sha256:5c5d0a3b48ceee0b48485e0c26037c0acd7d29765ca3fbb5cb3831d347423220", size = 70762, upload-time = "2020-12-28T15:15:30.155Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/a6/858897256d0deac81a172289110f31629fc4cee19b6f01283303e18c8db3/ptyprocess-0.7.0-py2.py3-none-any.whl", hash = "sha256:4b41f3967fce3af57cc7e94b888626c18bf37a083e3651ca8feeb66d492fef35", size = 13993, upload-time = "2020-12-28T15:15:28.35Z" }, +] + +[[package]] +name = "pure-eval" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/cd/05/0a34433a064256a578f1783a10da6df098ceaa4a57bbeaa96a6c0352786b/pure_eval-0.2.3.tar.gz", hash = "sha256:5f4e983f40564c576c7c8635ae88db5956bb2229d7e9237d03b3c0b0190eaf42", size = 19752, upload-time = "2024-07-21T12:58:21.801Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, +] + +[[package]] +name = "pycparser" +version = "2.23" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", hash = "sha256:78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2", size = 173734, upload-time = "2025-09-09T13:23:47.91Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", hash = "sha256:e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934", size = 118140, upload-time = "2025-09-09T13:23:46.651Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pydantic" +version = "2.12.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "annotated-types" }, + { name = "pydantic-core" }, + { name = "typing-extensions" }, + { name = "typing-inspection" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/69/44/36f1a6e523abc58ae5f928898e4aca2e0ea509b5aa6f6f392a5d882be928/pydantic-2.12.5.tar.gz", hash = "sha256:4d351024c75c0f085a9febbb665ce8c0c6ec5d30e903bdb6394b7ede26aebb49", size = 821591, upload-time = "2025-11-26T15:11:46.471Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5a/87/b70ad306ebb6f9b585f114d0ac2137d792b48be34d732d60e597c2f8465a/pydantic-2.12.5-py3-none-any.whl", hash = "sha256:e561593fccf61e8a20fc46dfc2dfe075b8be7d0188df33f221ad1f0139180f9d", size = 463580, upload-time = "2025-11-26T15:11:44.605Z" }, +] + +[[package]] +name = "pydantic-core" +version = "2.41.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/71/70/23b021c950c2addd24ec408e9ab05d59b035b39d97cdc1130e1bce647bb6/pydantic_core-2.41.5.tar.gz", hash = "sha256:08daa51ea16ad373ffd5e7606252cc32f07bc72b28284b6bc9c6df804816476e", size = 460952, upload-time = "2025-11-04T13:43:49.098Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c6/90/32c9941e728d564b411d574d8ee0cf09b12ec978cb22b294995bae5549a5/pydantic_core-2.41.5-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:77b63866ca88d804225eaa4af3e664c5faf3568cea95360d21f4725ab6e07146", size = 2107298, upload-time = "2025-11-04T13:39:04.116Z" }, + { url = "https://files.pythonhosted.org/packages/fb/a8/61c96a77fe28993d9a6fb0f4127e05430a267b235a124545d79fea46dd65/pydantic_core-2.41.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dfa8a0c812ac681395907e71e1274819dec685fec28273a28905df579ef137e2", size = 1901475, upload-time = "2025-11-04T13:39:06.055Z" }, + { url = "https://files.pythonhosted.org/packages/5d/b6/338abf60225acc18cdc08b4faef592d0310923d19a87fba1faf05af5346e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5921a4d3ca3aee735d9fd163808f5e8dd6c6972101e4adbda9a4667908849b97", size = 1918815, upload-time = "2025-11-04T13:39:10.41Z" }, + { url = "https://files.pythonhosted.org/packages/d1/1c/2ed0433e682983d8e8cba9c8d8ef274d4791ec6a6f24c58935b90e780e0a/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e25c479382d26a2a41b7ebea1043564a937db462816ea07afa8a44c0866d52f9", size = 2065567, upload-time = "2025-11-04T13:39:12.244Z" }, + { url = "https://files.pythonhosted.org/packages/b3/24/cf84974ee7d6eae06b9e63289b7b8f6549d416b5c199ca2d7ce13bbcf619/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f547144f2966e1e16ae626d8ce72b4cfa0caedc7fa28052001c94fb2fcaa1c52", size = 2230442, upload-time = "2025-11-04T13:39:13.962Z" }, + { url = "https://files.pythonhosted.org/packages/fd/21/4e287865504b3edc0136c89c9c09431be326168b1eb7841911cbc877a995/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6f52298fbd394f9ed112d56f3d11aabd0d5bd27beb3084cc3d8ad069483b8941", size = 2350956, upload-time = "2025-11-04T13:39:15.889Z" }, + { url = "https://files.pythonhosted.org/packages/a8/76/7727ef2ffa4b62fcab916686a68a0426b9b790139720e1934e8ba797e238/pydantic_core-2.41.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:100baa204bb412b74fe285fb0f3a385256dad1d1879f0a5cb1499ed2e83d132a", size = 2068253, upload-time = "2025-11-04T13:39:17.403Z" }, + { url = "https://files.pythonhosted.org/packages/d5/8c/a4abfc79604bcb4c748e18975c44f94f756f08fb04218d5cb87eb0d3a63e/pydantic_core-2.41.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:05a2c8852530ad2812cb7914dc61a1125dc4e06252ee98e5638a12da6cc6fb6c", size = 2177050, upload-time = "2025-11-04T13:39:19.351Z" }, + { url = "https://files.pythonhosted.org/packages/67/b1/de2e9a9a79b480f9cb0b6e8b6ba4c50b18d4e89852426364c66aa82bb7b3/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:29452c56df2ed968d18d7e21f4ab0ac55e71dc59524872f6fc57dcf4a3249ed2", size = 2147178, upload-time = "2025-11-04T13:39:21Z" }, + { url = "https://files.pythonhosted.org/packages/16/c1/dfb33f837a47b20417500efaa0378adc6635b3c79e8369ff7a03c494b4ac/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:d5160812ea7a8a2ffbe233d8da666880cad0cbaf5d4de74ae15c313213d62556", size = 2341833, upload-time = "2025-11-04T13:39:22.606Z" }, + { url = "https://files.pythonhosted.org/packages/47/36/00f398642a0f4b815a9a558c4f1dca1b4020a7d49562807d7bc9ff279a6c/pydantic_core-2.41.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:df3959765b553b9440adfd3c795617c352154e497a4eaf3752555cfb5da8fc49", size = 2321156, upload-time = "2025-11-04T13:39:25.843Z" }, + { url = "https://files.pythonhosted.org/packages/7e/70/cad3acd89fde2010807354d978725ae111ddf6d0ea46d1ea1775b5c1bd0c/pydantic_core-2.41.5-cp310-cp310-win32.whl", hash = "sha256:1f8d33a7f4d5a7889e60dc39856d76d09333d8a6ed0f5f1190635cbec70ec4ba", size = 1989378, upload-time = "2025-11-04T13:39:27.92Z" }, + { url = "https://files.pythonhosted.org/packages/76/92/d338652464c6c367e5608e4488201702cd1cbb0f33f7b6a85a60fe5f3720/pydantic_core-2.41.5-cp310-cp310-win_amd64.whl", hash = "sha256:62de39db01b8d593e45871af2af9e497295db8d73b085f6bfd0b18c83c70a8f9", size = 2013622, upload-time = "2025-11-04T13:39:29.848Z" }, + { url = "https://files.pythonhosted.org/packages/e8/72/74a989dd9f2084b3d9530b0915fdda64ac48831c30dbf7c72a41a5232db8/pydantic_core-2.41.5-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a3a52f6156e73e7ccb0f8cced536adccb7042be67cb45f9562e12b319c119da6", size = 2105873, upload-time = "2025-11-04T13:39:31.373Z" }, + { url = "https://files.pythonhosted.org/packages/12/44/37e403fd9455708b3b942949e1d7febc02167662bf1a7da5b78ee1ea2842/pydantic_core-2.41.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:7f3bf998340c6d4b0c9a2f02d6a400e51f123b59565d74dc60d252ce888c260b", size = 1899826, upload-time = "2025-11-04T13:39:32.897Z" }, + { url = "https://files.pythonhosted.org/packages/33/7f/1d5cab3ccf44c1935a359d51a8a2a9e1a654b744b5e7f80d41b88d501eec/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:378bec5c66998815d224c9ca994f1e14c0c21cb95d2f52b6021cc0b2a58f2a5a", size = 1917869, upload-time = "2025-11-04T13:39:34.469Z" }, + { url = "https://files.pythonhosted.org/packages/6e/6a/30d94a9674a7fe4f4744052ed6c5e083424510be1e93da5bc47569d11810/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e7b576130c69225432866fe2f4a469a85a54ade141d96fd396dffcf607b558f8", size = 2063890, upload-time = "2025-11-04T13:39:36.053Z" }, + { url = "https://files.pythonhosted.org/packages/50/be/76e5d46203fcb2750e542f32e6c371ffa9b8ad17364cf94bb0818dbfb50c/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6cb58b9c66f7e4179a2d5e0f849c48eff5c1fca560994d6eb6543abf955a149e", size = 2229740, upload-time = "2025-11-04T13:39:37.753Z" }, + { url = "https://files.pythonhosted.org/packages/d3/ee/fed784df0144793489f87db310a6bbf8118d7b630ed07aa180d6067e653a/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:88942d3a3dff3afc8288c21e565e476fc278902ae4d6d134f1eeda118cc830b1", size = 2350021, upload-time = "2025-11-04T13:39:40.94Z" }, + { url = "https://files.pythonhosted.org/packages/c8/be/8fed28dd0a180dca19e72c233cbf58efa36df055e5b9d90d64fd1740b828/pydantic_core-2.41.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f31d95a179f8d64d90f6831d71fa93290893a33148d890ba15de25642c5d075b", size = 2066378, upload-time = "2025-11-04T13:39:42.523Z" }, + { url = "https://files.pythonhosted.org/packages/b0/3b/698cf8ae1d536a010e05121b4958b1257f0b5522085e335360e53a6b1c8b/pydantic_core-2.41.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c1df3d34aced70add6f867a8cf413e299177e0c22660cc767218373d0779487b", size = 2175761, upload-time = "2025-11-04T13:39:44.553Z" }, + { url = "https://files.pythonhosted.org/packages/b8/ba/15d537423939553116dea94ce02f9c31be0fa9d0b806d427e0308ec17145/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:4009935984bd36bd2c774e13f9a09563ce8de4abaa7226f5108262fa3e637284", size = 2146303, upload-time = "2025-11-04T13:39:46.238Z" }, + { url = "https://files.pythonhosted.org/packages/58/7f/0de669bf37d206723795f9c90c82966726a2ab06c336deba4735b55af431/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:34a64bc3441dc1213096a20fe27e8e128bd3ff89921706e83c0b1ac971276594", size = 2340355, upload-time = "2025-11-04T13:39:48.002Z" }, + { url = "https://files.pythonhosted.org/packages/e5/de/e7482c435b83d7e3c3ee5ee4451f6e8973cff0eb6007d2872ce6383f6398/pydantic_core-2.41.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c9e19dd6e28fdcaa5a1de679aec4141f691023916427ef9bae8584f9c2fb3b0e", size = 2319875, upload-time = "2025-11-04T13:39:49.705Z" }, + { url = "https://files.pythonhosted.org/packages/fe/e6/8c9e81bb6dd7560e33b9053351c29f30c8194b72f2d6932888581f503482/pydantic_core-2.41.5-cp311-cp311-win32.whl", hash = "sha256:2c010c6ded393148374c0f6f0bf89d206bf3217f201faa0635dcd56bd1520f6b", size = 1987549, upload-time = "2025-11-04T13:39:51.842Z" }, + { url = "https://files.pythonhosted.org/packages/11/66/f14d1d978ea94d1bc21fc98fcf570f9542fe55bfcc40269d4e1a21c19bf7/pydantic_core-2.41.5-cp311-cp311-win_amd64.whl", hash = "sha256:76ee27c6e9c7f16f47db7a94157112a2f3a00e958bc626e2f4ee8bec5c328fbe", size = 2011305, upload-time = "2025-11-04T13:39:53.485Z" }, + { url = "https://files.pythonhosted.org/packages/56/d8/0e271434e8efd03186c5386671328154ee349ff0354d83c74f5caaf096ed/pydantic_core-2.41.5-cp311-cp311-win_arm64.whl", hash = "sha256:4bc36bbc0b7584de96561184ad7f012478987882ebf9f9c389b23f432ea3d90f", size = 1972902, upload-time = "2025-11-04T13:39:56.488Z" }, + { url = "https://files.pythonhosted.org/packages/5f/5d/5f6c63eebb5afee93bcaae4ce9a898f3373ca23df3ccaef086d0233a35a7/pydantic_core-2.41.5-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:f41a7489d32336dbf2199c8c0a215390a751c5b014c2c1c5366e817202e9cdf7", size = 2110990, upload-time = "2025-11-04T13:39:58.079Z" }, + { url = "https://files.pythonhosted.org/packages/aa/32/9c2e8ccb57c01111e0fd091f236c7b371c1bccea0fa85247ac55b1e2b6b6/pydantic_core-2.41.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:070259a8818988b9a84a449a2a7337c7f430a22acc0859c6b110aa7212a6d9c0", size = 1896003, upload-time = "2025-11-04T13:39:59.956Z" }, + { url = "https://files.pythonhosted.org/packages/68/b8/a01b53cb0e59139fbc9e4fda3e9724ede8de279097179be4ff31f1abb65a/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e96cea19e34778f8d59fe40775a7a574d95816eb150850a85a7a4c8f4b94ac69", size = 1919200, upload-time = "2025-11-04T13:40:02.241Z" }, + { url = "https://files.pythonhosted.org/packages/38/de/8c36b5198a29bdaade07b5985e80a233a5ac27137846f3bc2d3b40a47360/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ed2e99c456e3fadd05c991f8f437ef902e00eedf34320ba2b0842bd1c3ca3a75", size = 2052578, upload-time = "2025-11-04T13:40:04.401Z" }, + { url = "https://files.pythonhosted.org/packages/00/b5/0e8e4b5b081eac6cb3dbb7e60a65907549a1ce035a724368c330112adfdd/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:65840751b72fbfd82c3c640cff9284545342a4f1eb1586ad0636955b261b0b05", size = 2208504, upload-time = "2025-11-04T13:40:06.072Z" }, + { url = "https://files.pythonhosted.org/packages/77/56/87a61aad59c7c5b9dc8caad5a41a5545cba3810c3e828708b3d7404f6cef/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e536c98a7626a98feb2d3eaf75944ef6f3dbee447e1f841eae16f2f0a72d8ddc", size = 2335816, upload-time = "2025-11-04T13:40:07.835Z" }, + { url = "https://files.pythonhosted.org/packages/0d/76/941cc9f73529988688a665a5c0ecff1112b3d95ab48f81db5f7606f522d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eceb81a8d74f9267ef4081e246ffd6d129da5d87e37a77c9bde550cb04870c1c", size = 2075366, upload-time = "2025-11-04T13:40:09.804Z" }, + { url = "https://files.pythonhosted.org/packages/d3/43/ebef01f69baa07a482844faaa0a591bad1ef129253ffd0cdaa9d8a7f72d3/pydantic_core-2.41.5-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d38548150c39b74aeeb0ce8ee1d8e82696f4a4e16ddc6de7b1d8823f7de4b9b5", size = 2171698, upload-time = "2025-11-04T13:40:12.004Z" }, + { url = "https://files.pythonhosted.org/packages/b1/87/41f3202e4193e3bacfc2c065fab7706ebe81af46a83d3e27605029c1f5a6/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c23e27686783f60290e36827f9c626e63154b82b116d7fe9adba1fda36da706c", size = 2132603, upload-time = "2025-11-04T13:40:13.868Z" }, + { url = "https://files.pythonhosted.org/packages/49/7d/4c00df99cb12070b6bccdef4a195255e6020a550d572768d92cc54dba91a/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:482c982f814460eabe1d3bb0adfdc583387bd4691ef00b90575ca0d2b6fe2294", size = 2329591, upload-time = "2025-11-04T13:40:15.672Z" }, + { url = "https://files.pythonhosted.org/packages/cc/6a/ebf4b1d65d458f3cda6a7335d141305dfa19bdc61140a884d165a8a1bbc7/pydantic_core-2.41.5-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:bfea2a5f0b4d8d43adf9d7b8bf019fb46fdd10a2e5cde477fbcb9d1fa08c68e1", size = 2319068, upload-time = "2025-11-04T13:40:17.532Z" }, + { url = "https://files.pythonhosted.org/packages/49/3b/774f2b5cd4192d5ab75870ce4381fd89cf218af999515baf07e7206753f0/pydantic_core-2.41.5-cp312-cp312-win32.whl", hash = "sha256:b74557b16e390ec12dca509bce9264c3bbd128f8a2c376eaa68003d7f327276d", size = 1985908, upload-time = "2025-11-04T13:40:19.309Z" }, + { url = "https://files.pythonhosted.org/packages/86/45/00173a033c801cacf67c190fef088789394feaf88a98a7035b0e40d53dc9/pydantic_core-2.41.5-cp312-cp312-win_amd64.whl", hash = "sha256:1962293292865bca8e54702b08a4f26da73adc83dd1fcf26fbc875b35d81c815", size = 2020145, upload-time = "2025-11-04T13:40:21.548Z" }, + { url = "https://files.pythonhosted.org/packages/f9/22/91fbc821fa6d261b376a3f73809f907cec5ca6025642c463d3488aad22fb/pydantic_core-2.41.5-cp312-cp312-win_arm64.whl", hash = "sha256:1746d4a3d9a794cacae06a5eaaccb4b8643a131d45fbc9af23e353dc0a5ba5c3", size = 1976179, upload-time = "2025-11-04T13:40:23.393Z" }, + { url = "https://files.pythonhosted.org/packages/87/06/8806241ff1f70d9939f9af039c6c35f2360cf16e93c2ca76f184e76b1564/pydantic_core-2.41.5-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:941103c9be18ac8daf7b7adca8228f8ed6bb7a1849020f643b3a14d15b1924d9", size = 2120403, upload-time = "2025-11-04T13:40:25.248Z" }, + { url = "https://files.pythonhosted.org/packages/94/02/abfa0e0bda67faa65fef1c84971c7e45928e108fe24333c81f3bfe35d5f5/pydantic_core-2.41.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:112e305c3314f40c93998e567879e887a3160bb8689ef3d2c04b6cc62c33ac34", size = 1896206, upload-time = "2025-11-04T13:40:27.099Z" }, + { url = "https://files.pythonhosted.org/packages/15/df/a4c740c0943e93e6500f9eb23f4ca7ec9bf71b19e608ae5b579678c8d02f/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0cbaad15cb0c90aa221d43c00e77bb33c93e8d36e0bf74760cd00e732d10a6a0", size = 1919307, upload-time = "2025-11-04T13:40:29.806Z" }, + { url = "https://files.pythonhosted.org/packages/9a/e3/6324802931ae1d123528988e0e86587c2072ac2e5394b4bc2bc34b61ff6e/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:03ca43e12fab6023fc79d28ca6b39b05f794ad08ec2feccc59a339b02f2b3d33", size = 2063258, upload-time = "2025-11-04T13:40:33.544Z" }, + { url = "https://files.pythonhosted.org/packages/c9/d4/2230d7151d4957dd79c3044ea26346c148c98fbf0ee6ebd41056f2d62ab5/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dc799088c08fa04e43144b164feb0c13f9a0bc40503f8df3e9fde58a3c0c101e", size = 2214917, upload-time = "2025-11-04T13:40:35.479Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9f/eaac5df17a3672fef0081b6c1bb0b82b33ee89aa5cec0d7b05f52fd4a1fa/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:97aeba56665b4c3235a0e52b2c2f5ae9cd071b8a8310ad27bddb3f7fb30e9aa2", size = 2332186, upload-time = "2025-11-04T13:40:37.436Z" }, + { url = "https://files.pythonhosted.org/packages/cf/4e/35a80cae583a37cf15604b44240e45c05e04e86f9cfd766623149297e971/pydantic_core-2.41.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:406bf18d345822d6c21366031003612b9c77b3e29ffdb0f612367352aab7d586", size = 2073164, upload-time = "2025-11-04T13:40:40.289Z" }, + { url = "https://files.pythonhosted.org/packages/bf/e3/f6e262673c6140dd3305d144d032f7bd5f7497d3871c1428521f19f9efa2/pydantic_core-2.41.5-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:b93590ae81f7010dbe380cdeab6f515902ebcbefe0b9327cc4804d74e93ae69d", size = 2179146, upload-time = "2025-11-04T13:40:42.809Z" }, + { url = "https://files.pythonhosted.org/packages/75/c7/20bd7fc05f0c6ea2056a4565c6f36f8968c0924f19b7d97bbfea55780e73/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:01a3d0ab748ee531f4ea6c3e48ad9dac84ddba4b0d82291f87248f2f9de8d740", size = 2137788, upload-time = "2025-11-04T13:40:44.752Z" }, + { url = "https://files.pythonhosted.org/packages/3a/8d/34318ef985c45196e004bc46c6eab2eda437e744c124ef0dbe1ff2c9d06b/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:6561e94ba9dacc9c61bce40e2d6bdc3bfaa0259d3ff36ace3b1e6901936d2e3e", size = 2340133, upload-time = "2025-11-04T13:40:46.66Z" }, + { url = "https://files.pythonhosted.org/packages/9c/59/013626bf8c78a5a5d9350d12e7697d3d4de951a75565496abd40ccd46bee/pydantic_core-2.41.5-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:915c3d10f81bec3a74fbd4faebe8391013ba61e5a1a8d48c4455b923bdda7858", size = 2324852, upload-time = "2025-11-04T13:40:48.575Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d9/c248c103856f807ef70c18a4f986693a46a8ffe1602e5d361485da502d20/pydantic_core-2.41.5-cp313-cp313-win32.whl", hash = "sha256:650ae77860b45cfa6e2cdafc42618ceafab3a2d9a3811fcfbd3bbf8ac3c40d36", size = 1994679, upload-time = "2025-11-04T13:40:50.619Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8b/341991b158ddab181cff136acd2552c9f35bd30380422a639c0671e99a91/pydantic_core-2.41.5-cp313-cp313-win_amd64.whl", hash = "sha256:79ec52ec461e99e13791ec6508c722742ad745571f234ea6255bed38c6480f11", size = 2019766, upload-time = "2025-11-04T13:40:52.631Z" }, + { url = "https://files.pythonhosted.org/packages/73/7d/f2f9db34af103bea3e09735bb40b021788a5e834c81eedb541991badf8f5/pydantic_core-2.41.5-cp313-cp313-win_arm64.whl", hash = "sha256:3f84d5c1b4ab906093bdc1ff10484838aca54ef08de4afa9de0f5f14d69639cd", size = 1981005, upload-time = "2025-11-04T13:40:54.734Z" }, + { url = "https://files.pythonhosted.org/packages/ea/28/46b7c5c9635ae96ea0fbb779e271a38129df2550f763937659ee6c5dbc65/pydantic_core-2.41.5-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:3f37a19d7ebcdd20b96485056ba9e8b304e27d9904d233d7b1015db320e51f0a", size = 2119622, upload-time = "2025-11-04T13:40:56.68Z" }, + { url = "https://files.pythonhosted.org/packages/74/1a/145646e5687e8d9a1e8d09acb278c8535ebe9e972e1f162ed338a622f193/pydantic_core-2.41.5-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:1d1d9764366c73f996edd17abb6d9d7649a7eb690006ab6adbda117717099b14", size = 1891725, upload-time = "2025-11-04T13:40:58.807Z" }, + { url = "https://files.pythonhosted.org/packages/23/04/e89c29e267b8060b40dca97bfc64a19b2a3cf99018167ea1677d96368273/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:25e1c2af0fce638d5f1988b686f3b3ea8cd7de5f244ca147c777769e798a9cd1", size = 1915040, upload-time = "2025-11-04T13:41:00.853Z" }, + { url = "https://files.pythonhosted.org/packages/84/a3/15a82ac7bd97992a82257f777b3583d3e84bdb06ba6858f745daa2ec8a85/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:506d766a8727beef16b7adaeb8ee6217c64fc813646b424d0804d67c16eddb66", size = 2063691, upload-time = "2025-11-04T13:41:03.504Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/0046701313c6ef08c0c1cf0e028c67c770a4e1275ca73131563c5f2a310a/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4819fa52133c9aa3c387b3328f25c1facc356491e6135b459f1de698ff64d869", size = 2213897, upload-time = "2025-11-04T13:41:05.804Z" }, + { url = "https://files.pythonhosted.org/packages/8a/cd/6bac76ecd1b27e75a95ca3a9a559c643b3afcd2dd62086d4b7a32a18b169/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2b761d210c9ea91feda40d25b4efe82a1707da2ef62901466a42492c028553a2", size = 2333302, upload-time = "2025-11-04T13:41:07.809Z" }, + { url = "https://files.pythonhosted.org/packages/4c/d2/ef2074dc020dd6e109611a8be4449b98cd25e1b9b8a303c2f0fca2f2bcf7/pydantic_core-2.41.5-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:22f0fb8c1c583a3b6f24df2470833b40207e907b90c928cc8d3594b76f874375", size = 2064877, upload-time = "2025-11-04T13:41:09.827Z" }, + { url = "https://files.pythonhosted.org/packages/18/66/e9db17a9a763d72f03de903883c057b2592c09509ccfe468187f2a2eef29/pydantic_core-2.41.5-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2782c870e99878c634505236d81e5443092fba820f0373997ff75f90f68cd553", size = 2180680, upload-time = "2025-11-04T13:41:12.379Z" }, + { url = "https://files.pythonhosted.org/packages/d3/9e/3ce66cebb929f3ced22be85d4c2399b8e85b622db77dad36b73c5387f8f8/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:0177272f88ab8312479336e1d777f6b124537d47f2123f89cb37e0accea97f90", size = 2138960, upload-time = "2025-11-04T13:41:14.627Z" }, + { url = "https://files.pythonhosted.org/packages/a6/62/205a998f4327d2079326b01abee48e502ea739d174f0a89295c481a2272e/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:63510af5e38f8955b8ee5687740d6ebf7c2a0886d15a6d65c32814613681bc07", size = 2339102, upload-time = "2025-11-04T13:41:16.868Z" }, + { url = "https://files.pythonhosted.org/packages/3c/0d/f05e79471e889d74d3d88f5bd20d0ed189ad94c2423d81ff8d0000aab4ff/pydantic_core-2.41.5-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:e56ba91f47764cc14f1daacd723e3e82d1a89d783f0f5afe9c364b8bb491ccdb", size = 2326039, upload-time = "2025-11-04T13:41:18.934Z" }, + { url = "https://files.pythonhosted.org/packages/ec/e1/e08a6208bb100da7e0c4b288eed624a703f4d129bde2da475721a80cab32/pydantic_core-2.41.5-cp314-cp314-win32.whl", hash = "sha256:aec5cf2fd867b4ff45b9959f8b20ea3993fc93e63c7363fe6851424c8a7e7c23", size = 1995126, upload-time = "2025-11-04T13:41:21.418Z" }, + { url = "https://files.pythonhosted.org/packages/48/5d/56ba7b24e9557f99c9237e29f5c09913c81eeb2f3217e40e922353668092/pydantic_core-2.41.5-cp314-cp314-win_amd64.whl", hash = "sha256:8e7c86f27c585ef37c35e56a96363ab8de4e549a95512445b85c96d3e2f7c1bf", size = 2015489, upload-time = "2025-11-04T13:41:24.076Z" }, + { url = "https://files.pythonhosted.org/packages/4e/bb/f7a190991ec9e3e0ba22e4993d8755bbc4a32925c0b5b42775c03e8148f9/pydantic_core-2.41.5-cp314-cp314-win_arm64.whl", hash = "sha256:e672ba74fbc2dc8eea59fb6d4aed6845e6905fc2a8afe93175d94a83ba2a01a0", size = 1977288, upload-time = "2025-11-04T13:41:26.33Z" }, + { url = "https://files.pythonhosted.org/packages/92/ed/77542d0c51538e32e15afe7899d79efce4b81eee631d99850edc2f5e9349/pydantic_core-2.41.5-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:8566def80554c3faa0e65ac30ab0932b9e3a5cd7f8323764303d468e5c37595a", size = 2120255, upload-time = "2025-11-04T13:41:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/bb/3d/6913dde84d5be21e284439676168b28d8bbba5600d838b9dca99de0fad71/pydantic_core-2.41.5-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b80aa5095cd3109962a298ce14110ae16b8c1aece8b72f9dafe81cf597ad80b3", size = 1863760, upload-time = "2025-11-04T13:41:31.055Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f0/e5e6b99d4191da102f2b0eb9687aaa7f5bea5d9964071a84effc3e40f997/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3006c3dd9ba34b0c094c544c6006cc79e87d8612999f1a5d43b769b89181f23c", size = 1878092, upload-time = "2025-11-04T13:41:33.21Z" }, + { url = "https://files.pythonhosted.org/packages/71/48/36fb760642d568925953bcc8116455513d6e34c4beaa37544118c36aba6d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72f6c8b11857a856bcfa48c86f5368439f74453563f951e473514579d44aa612", size = 2053385, upload-time = "2025-11-04T13:41:35.508Z" }, + { url = "https://files.pythonhosted.org/packages/20/25/92dc684dd8eb75a234bc1c764b4210cf2646479d54b47bf46061657292a8/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5cb1b2f9742240e4bb26b652a5aeb840aa4b417c7748b6f8387927bc6e45e40d", size = 2218832, upload-time = "2025-11-04T13:41:37.732Z" }, + { url = "https://files.pythonhosted.org/packages/e2/09/f53e0b05023d3e30357d82eb35835d0f6340ca344720a4599cd663dca599/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bd3d54f38609ff308209bd43acea66061494157703364ae40c951f83ba99a1a9", size = 2327585, upload-time = "2025-11-04T13:41:40Z" }, + { url = "https://files.pythonhosted.org/packages/aa/4e/2ae1aa85d6af35a39b236b1b1641de73f5a6ac4d5a7509f77b814885760c/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ff4321e56e879ee8d2a879501c8e469414d948f4aba74a2d4593184eb326660", size = 2041078, upload-time = "2025-11-04T13:41:42.323Z" }, + { url = "https://files.pythonhosted.org/packages/cd/13/2e215f17f0ef326fc72afe94776edb77525142c693767fc347ed6288728d/pydantic_core-2.41.5-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d0d2568a8c11bf8225044aa94409e21da0cb09dcdafe9ecd10250b2baad531a9", size = 2173914, upload-time = "2025-11-04T13:41:45.221Z" }, + { url = "https://files.pythonhosted.org/packages/02/7a/f999a6dcbcd0e5660bc348a3991c8915ce6599f4f2c6ac22f01d7a10816c/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:a39455728aabd58ceabb03c90e12f71fd30fa69615760a075b9fec596456ccc3", size = 2129560, upload-time = "2025-11-04T13:41:47.474Z" }, + { url = "https://files.pythonhosted.org/packages/3a/b1/6c990ac65e3b4c079a4fb9f5b05f5b013afa0f4ed6780a3dd236d2cbdc64/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:239edca560d05757817c13dc17c50766136d21f7cd0fac50295499ae24f90fdf", size = 2329244, upload-time = "2025-11-04T13:41:49.992Z" }, + { url = "https://files.pythonhosted.org/packages/d9/02/3c562f3a51afd4d88fff8dffb1771b30cfdfd79befd9883ee094f5b6c0d8/pydantic_core-2.41.5-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:2a5e06546e19f24c6a96a129142a75cee553cc018ffee48a460059b1185f4470", size = 2331955, upload-time = "2025-11-04T13:41:54.079Z" }, + { url = "https://files.pythonhosted.org/packages/5c/96/5fb7d8c3c17bc8c62fdb031c47d77a1af698f1d7a406b0f79aaa1338f9ad/pydantic_core-2.41.5-cp314-cp314t-win32.whl", hash = "sha256:b4ececa40ac28afa90871c2cc2b9ffd2ff0bf749380fbdf57d165fd23da353aa", size = 1988906, upload-time = "2025-11-04T13:41:56.606Z" }, + { url = "https://files.pythonhosted.org/packages/22/ed/182129d83032702912c2e2d8bbe33c036f342cc735737064668585dac28f/pydantic_core-2.41.5-cp314-cp314t-win_amd64.whl", hash = "sha256:80aa89cad80b32a912a65332f64a4450ed00966111b6615ca6816153d3585a8c", size = 1981607, upload-time = "2025-11-04T13:41:58.889Z" }, + { url = "https://files.pythonhosted.org/packages/9f/ed/068e41660b832bb0b1aa5b58011dea2a3fe0ba7861ff38c4d4904c1c1a99/pydantic_core-2.41.5-cp314-cp314t-win_arm64.whl", hash = "sha256:35b44f37a3199f771c3eaa53051bc8a70cd7b54f333531c59e29fd4db5d15008", size = 1974769, upload-time = "2025-11-04T13:42:01.186Z" }, + { url = "https://files.pythonhosted.org/packages/54/db/160dffb57ed9a3705c4cbcbff0ac03bdae45f1ca7d58ab74645550df3fbd/pydantic_core-2.41.5-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:8bfeaf8735be79f225f3fefab7f941c712aaca36f1128c9d7e2352ee1aa87bdf", size = 2107999, upload-time = "2025-11-04T13:42:03.885Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7d/88e7de946f60d9263cc84819f32513520b85c0f8322f9b8f6e4afc938383/pydantic_core-2.41.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:346285d28e4c8017da95144c7f3acd42740d637ff41946af5ce6e5e420502dd5", size = 1929745, upload-time = "2025-11-04T13:42:06.075Z" }, + { url = "https://files.pythonhosted.org/packages/d5/c2/aef51e5b283780e85e99ff19db0f05842d2d4a8a8cd15e63b0280029b08f/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a75dafbf87d6276ddc5b2bf6fae5254e3d0876b626eb24969a574fff9149ee5d", size = 1920220, upload-time = "2025-11-04T13:42:08.457Z" }, + { url = "https://files.pythonhosted.org/packages/c7/97/492ab10f9ac8695cd76b2fdb24e9e61f394051df71594e9bcc891c9f586e/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7b93a4d08587e2b7e7882de461e82b6ed76d9026ce91ca7915e740ecc7855f60", size = 2067296, upload-time = "2025-11-04T13:42:10.817Z" }, + { url = "https://files.pythonhosted.org/packages/ec/23/984149650e5269c59a2a4c41d234a9570adc68ab29981825cfaf4cfad8f4/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8465ab91a4bd96d36dde3263f06caa6a8a6019e4113f24dc753d79a8b3a3f82", size = 2231548, upload-time = "2025-11-04T13:42:13.843Z" }, + { url = "https://files.pythonhosted.org/packages/71/0c/85bcbb885b9732c28bec67a222dbed5ed2d77baee1f8bba2002e8cd00c5c/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:299e0a22e7ae2b85c1a57f104538b2656e8ab1873511fd718a1c1c6f149b77b5", size = 2362571, upload-time = "2025-11-04T13:42:16.208Z" }, + { url = "https://files.pythonhosted.org/packages/c0/4a/412d2048be12c334003e9b823a3fa3d038e46cc2d64dd8aab50b31b65499/pydantic_core-2.41.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:707625ef0983fcfb461acfaf14de2067c5942c6bb0f3b4c99158bed6fedd3cf3", size = 2068175, upload-time = "2025-11-04T13:42:18.911Z" }, + { url = "https://files.pythonhosted.org/packages/73/f4/c58b6a776b502d0a5540ad02e232514285513572060f0d78f7832ca3c98b/pydantic_core-2.41.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f41eb9797986d6ebac5e8edff36d5cef9de40def462311b3eb3eeded1431e425", size = 2177203, upload-time = "2025-11-04T13:42:22.578Z" }, + { url = "https://files.pythonhosted.org/packages/ed/ae/f06ea4c7e7a9eead3d165e7623cd2ea0cb788e277e4f935af63fc98fa4e6/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:0384e2e1021894b1ff5a786dbf94771e2986ebe2869533874d7e43bc79c6f504", size = 2148191, upload-time = "2025-11-04T13:42:24.89Z" }, + { url = "https://files.pythonhosted.org/packages/c1/57/25a11dcdc656bf5f8b05902c3c2934ac3ea296257cc4a3f79a6319e61856/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:f0cd744688278965817fd0839c4a4116add48d23890d468bc436f78beb28abf5", size = 2343907, upload-time = "2025-11-04T13:42:27.683Z" }, + { url = "https://files.pythonhosted.org/packages/96/82/e33d5f4933d7a03327c0c43c65d575e5919d4974ffc026bc917a5f7b9f61/pydantic_core-2.41.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:753e230374206729bf0a807954bcc6c150d3743928a73faffee51ac6557a03c3", size = 2322174, upload-time = "2025-11-04T13:42:30.776Z" }, + { url = "https://files.pythonhosted.org/packages/81/45/4091be67ce9f469e81656f880f3506f6a5624121ec5eb3eab37d7581897d/pydantic_core-2.41.5-cp39-cp39-win32.whl", hash = "sha256:873e0d5b4fb9b89ef7c2d2a963ea7d02879d9da0da8d9d4933dee8ee86a8b460", size = 1990353, upload-time = "2025-11-04T13:42:33.111Z" }, + { url = "https://files.pythonhosted.org/packages/44/8a/a98aede18db6e9cd5d66bcacd8a409fcf8134204cdede2e7de35c5a2c5ef/pydantic_core-2.41.5-cp39-cp39-win_amd64.whl", hash = "sha256:e4f4a984405e91527a0d62649ee21138f8e3d0ef103be488c1dc11a80d7f184b", size = 2015698, upload-time = "2025-11-04T13:42:35.484Z" }, + { url = "https://files.pythonhosted.org/packages/11/72/90fda5ee3b97e51c494938a4a44c3a35a9c96c19bba12372fb9c634d6f57/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:b96d5f26b05d03cc60f11a7761a5ded1741da411e7fe0909e27a5e6a0cb7b034", size = 2115441, upload-time = "2025-11-04T13:42:39.557Z" }, + { url = "https://files.pythonhosted.org/packages/1f/53/8942f884fa33f50794f119012dc6a1a02ac43a56407adaac20463df8e98f/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:634e8609e89ceecea15e2d61bc9ac3718caaaa71963717bf3c8f38bfde64242c", size = 1930291, upload-time = "2025-11-04T13:42:42.169Z" }, + { url = "https://files.pythonhosted.org/packages/79/c8/ecb9ed9cd942bce09fc888ee960b52654fbdbede4ba6c2d6e0d3b1d8b49c/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:93e8740d7503eb008aa2df04d3b9735f845d43ae845e6dcd2be0b55a2da43cd2", size = 1948632, upload-time = "2025-11-04T13:42:44.564Z" }, + { url = "https://files.pythonhosted.org/packages/2e/1b/687711069de7efa6af934e74f601e2a4307365e8fdc404703afc453eab26/pydantic_core-2.41.5-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f15489ba13d61f670dcc96772e733aad1a6f9c429cc27574c6cdaed82d0146ad", size = 2138905, upload-time = "2025-11-04T13:42:47.156Z" }, + { url = "https://files.pythonhosted.org/packages/09/32/59b0c7e63e277fa7911c2fc70ccfb45ce4b98991e7ef37110663437005af/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:7da7087d756b19037bc2c06edc6c170eeef3c3bafcb8f532ff17d64dc427adfd", size = 2110495, upload-time = "2025-11-04T13:42:49.689Z" }, + { url = "https://files.pythonhosted.org/packages/aa/81/05e400037eaf55ad400bcd318c05bb345b57e708887f07ddb2d20e3f0e98/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:aabf5777b5c8ca26f7824cb4a120a740c9588ed58df9b2d196ce92fba42ff8dc", size = 1915388, upload-time = "2025-11-04T13:42:52.215Z" }, + { url = "https://files.pythonhosted.org/packages/6e/0d/e3549b2399f71d56476b77dbf3cf8937cec5cd70536bdc0e374a421d0599/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c007fe8a43d43b3969e8469004e9845944f1a80e6acd47c150856bb87f230c56", size = 1942879, upload-time = "2025-11-04T13:42:56.483Z" }, + { url = "https://files.pythonhosted.org/packages/f7/07/34573da085946b6a313d7c42f82f16e8920bfd730665de2d11c0c37a74b5/pydantic_core-2.41.5-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76d0819de158cd855d1cbb8fcafdf6f5cf1eb8e470abe056d5d161106e38062b", size = 2139017, upload-time = "2025-11-04T13:42:59.471Z" }, + { url = "https://files.pythonhosted.org/packages/e6/b0/1a2aa41e3b5a4ba11420aba2d091b2d17959c8d1519ece3627c371951e73/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b5819cd790dbf0c5eb9f82c73c16b39a65dd6dd4d1439dcdea7816ec9adddab8", size = 2103351, upload-time = "2025-11-04T13:43:02.058Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ee/31b1f0020baaf6d091c87900ae05c6aeae101fa4e188e1613c80e4f1ea31/pydantic_core-2.41.5-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5a4e67afbc95fa5c34cf27d9089bca7fcab4e51e57278d710320a70b956d1b9a", size = 1925363, upload-time = "2025-11-04T13:43:05.159Z" }, + { url = "https://files.pythonhosted.org/packages/e1/89/ab8e86208467e467a80deaca4e434adac37b10a9d134cd2f99b28a01e483/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ece5c59f0ce7d001e017643d8d24da587ea1f74f6993467d85ae8a5ef9d4f42b", size = 2135615, upload-time = "2025-11-04T13:43:08.116Z" }, + { url = "https://files.pythonhosted.org/packages/99/0a/99a53d06dd0348b2008f2f30884b34719c323f16c3be4e6cc1203b74a91d/pydantic_core-2.41.5-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:16f80f7abe3351f8ea6858914ddc8c77e02578544a0ebc15b4c2e1a0e813b0b2", size = 2175369, upload-time = "2025-11-04T13:43:12.49Z" }, + { url = "https://files.pythonhosted.org/packages/6d/94/30ca3b73c6d485b9bb0bc66e611cff4a7138ff9736b7e66bcf0852151636/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:33cb885e759a705b426baada1fe68cbb0a2e68e34c5d0d0289a364cf01709093", size = 2144218, upload-time = "2025-11-04T13:43:15.431Z" }, + { url = "https://files.pythonhosted.org/packages/87/57/31b4f8e12680b739a91f472b5671294236b82586889ef764b5fbc6669238/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c8d8b4eb992936023be7dee581270af5c6e0697a8559895f527f5b7105ecd36a", size = 2329951, upload-time = "2025-11-04T13:43:18.062Z" }, + { url = "https://files.pythonhosted.org/packages/7d/73/3c2c8edef77b8f7310e6fb012dbc4b8551386ed575b9eb6fb2506e28a7eb/pydantic_core-2.41.5-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:242a206cd0318f95cd21bdacff3fcc3aab23e79bba5cac3db5a841c9ef9c6963", size = 2318428, upload-time = "2025-11-04T13:43:20.679Z" }, + { url = "https://files.pythonhosted.org/packages/2f/02/8559b1f26ee0d502c74f9cca5c0d2fd97e967e083e006bbbb4e97f3a043a/pydantic_core-2.41.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d3a978c4f57a597908b7e697229d996d77a6d3c94901e9edee593adada95ce1a", size = 2147009, upload-time = "2025-11-04T13:43:23.286Z" }, + { url = "https://files.pythonhosted.org/packages/5f/9b/1b3f0e9f9305839d7e84912f9e8bfbd191ed1b1ef48083609f0dabde978c/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b2379fa7ed44ddecb5bfe4e48577d752db9fc10be00a6b7446e9663ba143de26", size = 2101980, upload-time = "2025-11-04T13:43:25.97Z" }, + { url = "https://files.pythonhosted.org/packages/a4/ed/d71fefcb4263df0da6a85b5d8a7508360f2f2e9b3bf5814be9c8bccdccc1/pydantic_core-2.41.5-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:266fb4cbf5e3cbd0b53669a6d1b039c45e3ce651fd5442eff4d07c2cc8d66808", size = 1923865, upload-time = "2025-11-04T13:43:28.763Z" }, + { url = "https://files.pythonhosted.org/packages/ce/3a/626b38db460d675f873e4444b4bb030453bbe7b4ba55df821d026a0493c4/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58133647260ea01e4d0500089a8c4f07bd7aa6ce109682b1426394988d8aaacc", size = 2134256, upload-time = "2025-11-04T13:43:31.71Z" }, + { url = "https://files.pythonhosted.org/packages/83/d9/8412d7f06f616bbc053d30cb4e5f76786af3221462ad5eee1f202021eb4e/pydantic_core-2.41.5-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:287dad91cfb551c363dc62899a80e9e14da1f0e2b6ebde82c806612ca2a13ef1", size = 2174762, upload-time = "2025-11-04T13:43:34.744Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/162d906b8e3ba3a99354e20faa1b49a85206c47de97a639510a0e673f5da/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:03b77d184b9eb40240ae9fd676ca364ce1085f203e1b1256f8ab9984dca80a84", size = 2143141, upload-time = "2025-11-04T13:43:37.701Z" }, + { url = "https://files.pythonhosted.org/packages/1f/f2/f11dd73284122713f5f89fc940f370d035fa8e1e078d446b3313955157fe/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:a668ce24de96165bb239160b3d854943128f4334822900534f2fe947930e5770", size = 2330317, upload-time = "2025-11-04T13:43:40.406Z" }, + { url = "https://files.pythonhosted.org/packages/88/9d/b06ca6acfe4abb296110fb1273a4d848a0bfb2ff65f3ee92127b3244e16b/pydantic_core-2.41.5-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:f14f8f046c14563f8eb3f45f499cc658ab8d10072961e07225e507adb700e93f", size = 2316992, upload-time = "2025-11-04T13:43:43.602Z" }, + { url = "https://files.pythonhosted.org/packages/36/c7/cfc8e811f061c841d7990b0201912c3556bfeb99cdcb7ed24adc8d6f8704/pydantic_core-2.41.5-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:56121965f7a4dc965bff783d70b907ddf3d57f6eba29b6d2e5dabfaf07799c51", size = 2145302, upload-time = "2025-11-04T13:43:46.64Z" }, +] + +[[package]] +name = "pyelftools" +version = "0.32" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b9/ab/33968940b2deb3d92f5b146bc6d4009a5f95d1d06c148ea2f9ee965071af/pyelftools-0.32.tar.gz", hash = "sha256:6de90ee7b8263e740c8715a925382d4099b354f29ac48ea40d840cf7aa14ace5", size = 15047199, upload-time = "2025-02-19T14:20:05.549Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/af/43/700932c4f0638c3421177144a2e86448c0d75dbaee2c7936bda3f9fd0878/pyelftools-0.32-py3-none-any.whl", hash = "sha256:013df952a006db5e138b1edf6d8a68ecc50630adbd0d83a2d41e7f846163d738", size = 188525, upload-time = "2025-02-19T14:19:59.919Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.13.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515, upload-time = "2026-05-21T19:54:36.618Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274, upload-time = "2026-05-21T19:54:35.362Z" }, +] + +[[package]] +name = "pyparsing" +version = "3.3.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" }, +] + +[[package]] +name = "pyproject-hooks" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e7/82/28175b2414effca1cdac8dc99f76d660e7a4fb0ceefa4b4ab8f5f6742925/pyproject_hooks-1.2.0.tar.gz", hash = "sha256:1e859bd5c40fae9448642dd871adf459e5e2084186e8d2c2a79a824c970da1f8", size = 19228, upload-time = "2024-09-29T09:24:13.293Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/bd/24/12818598c362d7f300f18e74db45963dbcb85150324092410c8b49405e42/pyproject_hooks-1.2.0-py3-none-any.whl", hash = "sha256:9e5c6bfa8dcc30091c74b0cf803c81fdd29d94f01992a7707bc97babb1141913", size = 10216, upload-time = "2024-09-29T09:24:11.978Z" }, +] + +[[package]] +name = "pytest" +version = "8.4.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version < '3.10'" }, + { name = "iniconfig", version = "2.1.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "packaging", marker = "python_full_version < '3.10'" }, + { name = "pluggy", marker = "python_full_version < '3.10'" }, + { name = "pygments", marker = "python_full_version < '3.10'" }, + { name = "tomli", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a3/5c/00a0e072241553e1a7496d638deababa67c5058571567b92a7eaa258397c/pytest-8.4.2.tar.gz", hash = "sha256:86c0d0b93306b961d58d62a4db4879f27fe25513d4b969df351abdddb3c30e01", size = 1519618, upload-time = "2025-09-04T14:34:22.711Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/a8/a4/20da314d277121d6534b3a980b29035dcd51e6744bd79075a6ce8fa4eb8d/pytest-8.4.2-py3-none-any.whl", hash = "sha256:872f880de3fc3a5bdc88a11b39c9710c3497a547cfa9320bc3c5e62fbf272e79", size = 365750, upload-time = "2025-09-04T14:34:20.226Z" }, +] + +[[package]] +name = "pytest" +version = "9.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "colorama", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, + { name = "exceptiongroup", marker = "python_full_version == '3.10.*'" }, + { name = "iniconfig", version = "2.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "packaging", marker = "python_full_version >= '3.10'" }, + { name = "pluggy", marker = "python_full_version >= '3.10'" }, + { name = "pygments", marker = "python_full_version >= '3.10'" }, + { name = "tomli", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7d/0d/549bd94f1a0a402dc8cf64563a117c0f3765662e2e668477624baeec44d5/pytest-9.0.3.tar.gz", hash = "sha256:b86ada508af81d19edeb213c681b1d48246c1a91d304c6c81a427674c17eb91c", size = 1572165, upload-time = "2026-04-07T17:16:18.027Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d4/24/a372aaf5c9b7208e7112038812994107bc65a84cd00e0354a88c2c77a617/pytest-9.0.3-py3-none-any.whl", hash = "sha256:2c5efc453d45394fdd706ade797c0a81091eccd1d6e4bccfcd476e2b8e0ab5d9", size = 375249, upload-time = "2026-04-07T17:16:16.13Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version < '3.10'" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/42/86/9e3c5f48f7b7b638b216e4b9e645f54d199d7abbbab7a64a13b4e12ba10f/pytest_asyncio-1.2.0.tar.gz", hash = "sha256:c609a64a2a8768462d0c99811ddb8bd2583c33fd33cf7f21af1c142e824ffb57", size = 50119, upload-time = "2025-09-12T07:33:53.816Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/93/2fa34714b7a4ae72f2f8dad66ba17dd9a2c793220719e736dda28b7aec27/pytest_asyncio-1.2.0-py3-none-any.whl", hash = "sha256:8e17ae5e46d8e7efe51ab6494dd2010f4ca8dae51652aa3c8d55acf50bfb2e99", size = 15095, upload-time = "2025-09-12T07:33:52.639Z" }, +] + +[[package]] +name = "pytest-asyncio" +version = "1.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "backports-asyncio-runner", marker = "python_full_version == '3.10.*'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version >= '3.10' and python_full_version < '3.13'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/90/2c/8af215c0f776415f3590cac4f9086ccefd6fd463befeae41cd4d3f193e5a/pytest_asyncio-1.3.0.tar.gz", hash = "sha256:d7f52f36d231b80ee124cd216ffb19369aa168fc10095013c6b014a34d3ee9e5", size = 50087, upload-time = "2025-11-10T16:07:47.256Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e5/35/f8b19922b6a25bc0880171a2f1a003eaeb93657475193ab516fd87cac9da/pytest_asyncio-1.3.0-py3-none-any.whl", hash = "sha256:611e26147c7f77640e6d0a92a38ed17c3e9848063698d5c93d5aa7aa11cebff5", size = 15075, upload-time = "2025-11-10T16:07:45.537Z" }, +] + +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + +[[package]] +name = "python-sdks" +version = "0.1.0" +source = { virtual = "." } +dependencies = [ + { name = "livekit" }, + { name = "livekit-api" }, + { name = "livekit-protocol" }, +] + +[package.dev-dependencies] +dev = [ + { name = "auditwheel", version = "6.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10' and sys_platform == 'linux'" }, + { name = "auditwheel", version = "6.6.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10' and sys_platform == 'linux'" }, + { name = "cibuildwheel", version = "2.23.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, + { name = "cibuildwheel", version = "3.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "ipython", version = "8.18.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "ipython", version = "8.38.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "ipython", version = "9.10.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "matplotlib", version = "3.9.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "matplotlib", version = "3.10.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mypy" }, + { name = "numpy", version = "2.0.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "numpy", version = "2.2.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "numpy", version = "2.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "pydantic" }, + { name = "pytest", version = "8.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pytest", version = "9.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pytest-asyncio", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "pytest-asyncio", version = "1.3.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "requests" }, + { name = "ruff" }, + { name = "twine" }, + { name = "types-aiofiles" }, + { name = "wheel" }, +] + +[package.metadata] +requires-dist = [ + { name = "livekit", editable = "livekit-rtc" }, + { name = "livekit-api", editable = "livekit-api" }, + { name = "livekit-protocol", editable = "livekit-protocol" }, +] + +[package.metadata.requires-dev] +dev = [ + { name = "auditwheel", marker = "sys_platform == 'linux'" }, + { name = "cibuildwheel" }, + { name = "ipython", specifier = ">=8.0.0" }, + { name = "matplotlib" }, + { name = "mypy", specifier = ">=1.13.0" }, + { name = "numpy" }, + { name = "pydantic" }, + { name = "pytest", specifier = ">=8.3.4" }, + { name = "pytest-asyncio", specifier = ">=0.24.0" }, + { name = "requests" }, + { name = "ruff", specifier = ">=0.8.5" }, + { name = "twine" }, + { name = "types-aiofiles", specifier = ">=24" }, + { name = "wheel" }, +] + +[[package]] +name = "pywin32-ctypes" +version = "0.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/9f/01a1a99704853cb63f253eea009390c88e7131c67e66a0a02099a8c917cb/pywin32-ctypes-0.2.3.tar.gz", hash = "sha256:d162dc04946d704503b2edc4d55f3dba5c1d539ead017afa00142c38b9885755", size = 29471, upload-time = "2024-08-14T10:15:34.626Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/3d/8161f7711c017e01ac9f008dfddd9410dff3674334c233bde66e7ba65bbf/pywin32_ctypes-0.2.3-py3-none-any.whl", hash = "sha256:8a1513379d709975552d202d942d9837758905c8d01eb82b8bcc30918929e7b8", size = 30756, upload-time = "2024-08-14T10:15:33.187Z" }, +] + +[[package]] +name = "readme-renderer" +version = "44.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "nh3" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5a/a9/104ec9234c8448c4379768221ea6df01260cd6c2ce13182d4eac531c8342/readme_renderer-44.0.tar.gz", hash = "sha256:8712034eabbfa6805cacf1402b4eeb2a73028f72d1166d6f5cb7f9c047c5d1e1", size = 32056, upload-time = "2024-07-08T15:00:57.805Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e1/67/921ec3024056483db83953ae8e48079ad62b92db7880013ca77632921dd0/readme_renderer-44.0-py3-none-any.whl", hash = "sha256:2fbca89b81a08526aadf1357a8c2ae889ec05fb03f5da67f9769c9a592166151", size = 13310, upload-time = "2024-07-08T15:00:56.577Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "requests-toolbelt" +version = "1.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f3/61/d7545dafb7ac2230c70d38d31cbfe4cc64f7144dc41f6e4e4b78ecd9f5bb/requests-toolbelt-1.0.0.tar.gz", hash = "sha256:7681a0a3d047012b5bdc0ee37d7f8f07ebe76ab08caeccfc3921ce23c88d5bc6", size = 206888, upload-time = "2023-05-01T04:11:33.229Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/51/d4db610ef29373b879047326cbf6fa98b6c1969d6f6dc423279de2b1be2c/requests_toolbelt-1.0.0-py2.py3-none-any.whl", hash = "sha256:cccfdd665f0a24fcf4726e690f65639d272bb0637b9b92dfd91a5568ccf6bd06", size = 54481, upload-time = "2023-05-01T04:11:28.427Z" }, +] + +[[package]] +name = "rfc3986" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/85/40/1520d68bfa07ab5a6f065a186815fb6610c86fe957bc065754e47f7b0840/rfc3986-2.0.0.tar.gz", hash = "sha256:97aacf9dbd4bfd829baad6e6309fa6573aaf1be3f6fa735c8ab05e46cecb261c", size = 49026, upload-time = "2022-01-10T00:52:30.832Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ff/9a/9afaade874b2fa6c752c36f1548f718b5b83af81ed9b76628329dab81c1b/rfc3986-2.0.0-py2.py3-none-any.whl", hash = "sha256:50b1502b60e289cb37883f3dfd34532b8873c7de9f49bb546641ce9cbd256ebd", size = 31326, upload-time = "2022-01-10T00:52:29.594Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/da/31/d6e536cdebb6568ae75a7f00e4b4819ae0ad2640c3604c305a0428680b0c/ruff-0.15.4.tar.gz", hash = "sha256:3412195319e42d634470cc97aa9803d07e9d5c9223b99bcb1518f0c725f26ae1", size = 4569550, upload-time = "2026-02-26T20:04:14.959Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f2/82/c11a03cfec3a4d26a0ea1e571f0f44be5993b923f905eeddfc397c13d360/ruff-0.15.4-py3-none-linux_armv6l.whl", hash = "sha256:a1810931c41606c686bae8b5b9a8072adac2f611bb433c0ba476acba17a332e0", size = 10453333, upload-time = "2026-02-26T20:04:20.093Z" }, + { url = "https://files.pythonhosted.org/packages/ce/5d/6a1f271f6e31dffb31855996493641edc3eef8077b883eaf007a2f1c2976/ruff-0.15.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:5a1632c66672b8b4d3e1d1782859e98d6e0b4e70829530666644286600a33992", size = 10853356, upload-time = "2026-02-26T20:04:05.808Z" }, + { url = "https://files.pythonhosted.org/packages/b1/d8/0fab9f8842b83b1a9c2bf81b85063f65e93fb512e60effa95b0be49bfc54/ruff-0.15.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a4386ba2cd6c0f4ff75252845906acc7c7c8e1ac567b7bc3d373686ac8c222ba", size = 10187434, upload-time = "2026-02-26T20:03:54.656Z" }, + { url = "https://files.pythonhosted.org/packages/85/cc/cc220fd9394eff5db8d94dec199eec56dd6c9f3651d8869d024867a91030/ruff-0.15.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2496488bdfd3732747558b6f95ae427ff066d1fcd054daf75f5a50674411e75", size = 10535456, upload-time = "2026-02-26T20:03:52.738Z" }, + { url = "https://files.pythonhosted.org/packages/fa/0f/bced38fa5cf24373ec767713c8e4cadc90247f3863605fb030e597878661/ruff-0.15.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3f1c4893841ff2d54cbda1b2860fa3260173df5ddd7b95d370186f8a5e66a4ac", size = 10287772, upload-time = "2026-02-26T20:04:08.138Z" }, + { url = "https://files.pythonhosted.org/packages/2b/90/58a1802d84fed15f8f281925b21ab3cecd813bde52a8ca033a4de8ab0e7a/ruff-0.15.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:820b8766bd65503b6c30aaa6331e8ef3a6e564f7999c844e9a547c40179e440a", size = 11049051, upload-time = "2026-02-26T20:04:03.53Z" }, + { url = "https://files.pythonhosted.org/packages/d2/ac/b7ad36703c35f3866584564dc15f12f91cb1a26a897dc2fd13d7cb3ae1af/ruff-0.15.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c9fb74bab47139c1751f900f857fa503987253c3ef89129b24ed375e72873e85", size = 11890494, upload-time = "2026-02-26T20:04:10.497Z" }, + { url = "https://files.pythonhosted.org/packages/93/3d/3eb2f47a39a8b0da99faf9c54d3eb24720add1e886a5309d4d1be73a6380/ruff-0.15.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f80c98765949c518142b3a50a5db89343aa90f2c2bf7799de9986498ae6176db", size = 11326221, upload-time = "2026-02-26T20:04:12.84Z" }, + { url = "https://files.pythonhosted.org/packages/ff/90/bf134f4c1e5243e62690e09d63c55df948a74084c8ac3e48a88468314da6/ruff-0.15.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:451a2e224151729b3b6c9ffb36aed9091b2996fe4bdbd11f47e27d8f2e8888ec", size = 11168459, upload-time = "2026-02-26T20:04:00.969Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e5/a64d27688789b06b5d55162aafc32059bb8c989c61a5139a36e1368285eb/ruff-0.15.4-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:a8f157f2e583c513c4f5f896163a93198297371f34c04220daf40d133fdd4f7f", size = 11104366, upload-time = "2026-02-26T20:03:48.099Z" }, + { url = "https://files.pythonhosted.org/packages/f1/f6/32d1dcb66a2559763fc3027bdd65836cad9eb09d90f2ed6a63d8e9252b02/ruff-0.15.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:917cc68503357021f541e69b35361c99387cdbbf99bd0ea4aa6f28ca99ff5338", size = 10510887, upload-time = "2026-02-26T20:03:45.771Z" }, + { url = "https://files.pythonhosted.org/packages/ff/92/22d1ced50971c5b6433aed166fcef8c9343f567a94cf2b9d9089f6aa80fe/ruff-0.15.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:e9737c8161da79fd7cfec19f1e35620375bd8b2a50c3e77fa3d2c16f574105cc", size = 10285939, upload-time = "2026-02-26T20:04:22.42Z" }, + { url = "https://files.pythonhosted.org/packages/e6/f4/7c20aec3143837641a02509a4668fb146a642fd1211846634edc17eb5563/ruff-0.15.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:291258c917539e18f6ba40482fe31d6f5ac023994ee11d7bdafd716f2aab8a68", size = 10765471, upload-time = "2026-02-26T20:03:58.924Z" }, + { url = "https://files.pythonhosted.org/packages/d0/09/6d2f7586f09a16120aebdff8f64d962d7c4348313c77ebb29c566cefc357/ruff-0.15.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3f83c45911da6f2cd5936c436cf86b9f09f09165f033a99dcf7477e34041cbc3", size = 11263382, upload-time = "2026-02-26T20:04:24.424Z" }, + { url = "https://files.pythonhosted.org/packages/1b/fa/2ef715a1cd329ef47c1a050e10dee91a9054b7ce2fcfdd6a06d139afb7ec/ruff-0.15.4-py3-none-win32.whl", hash = "sha256:65594a2d557d4ee9f02834fcdf0a28daa8b3b9f6cb2cb93846025a36db47ef22", size = 10506664, upload-time = "2026-02-26T20:03:50.56Z" }, + { url = "https://files.pythonhosted.org/packages/d0/a8/c688ef7e29983976820d18710f955751d9f4d4eb69df658af3d006e2ba3e/ruff-0.15.4-py3-none-win_amd64.whl", hash = "sha256:04196ad44f0df220c2ece5b0e959c2f37c777375ec744397d21d15b50a75264f", size = 11651048, upload-time = "2026-02-26T20:04:17.191Z" }, + { url = "https://files.pythonhosted.org/packages/3e/0a/9e1be9035b37448ce2e68c978f0591da94389ade5a5abafa4cf99985d1b2/ruff-0.15.4-py3-none-win_arm64.whl", hash = "sha256:60d5177e8cfc70e51b9c5fad936c634872a74209f934c1e79107d11787ad5453", size = 10966776, upload-time = "2026-02-26T20:03:56.908Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.3.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +dependencies = [ + { name = "cryptography", marker = "python_full_version < '3.10'" }, + { name = "jeepney", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/53/a4/f48c9d79cb507ed1373477dbceaba7401fd8a23af63b837fa61f1dcd3691/SecretStorage-3.3.3.tar.gz", hash = "sha256:2403533ef369eca6d2ba81718576c5e0f564d5cca1b58f73a8b23e7d4eeebd77", size = 19739, upload-time = "2022-08-13T16:22:46.976Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/24/b4293291fa1dd830f353d2cb163295742fa87f179fcc8a20a306a81978b7/SecretStorage-3.3.3-py3-none-any.whl", hash = "sha256:f356e6628222568e3af06f2eba8df495efa13b3b63081dafd4f7d9a7b7bc9f99", size = 15221, upload-time = "2022-08-13T16:22:44.457Z" }, +] + +[[package]] +name = "secretstorage" +version = "3.5.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "cryptography", marker = "python_full_version >= '3.10'" }, + { name = "jeepney", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1c/03/e834bcd866f2f8a49a85eaff47340affa3bfa391ee9912a952a1faa68c7b/secretstorage-3.5.0.tar.gz", hash = "sha256:f04b8e4689cbce351744d5537bf6b1329c6fc68f91fa666f60a380edddcd11be", size = 19884, upload-time = "2025-11-23T19:02:53.191Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/46/f5af3402b579fd5e11573ce652019a67074317e18c1935cc0b4ba9b35552/secretstorage-3.5.0-py3-none-any.whl", hash = "sha256:0ce65888c0725fcb2c5bc0fdb8e5438eece02c523557ea40ce0703c266248137", size = 15554, upload-time = "2025-11-23T19:02:51.545Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "stack-data" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "asttokens" }, + { name = "executing" }, + { name = "pure-eval" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/28/e3/55dcc2cfbc3ca9c29519eb6884dd1415ecb53b0e934862d3559ddcb7e20b/stack_data-0.6.3.tar.gz", hash = "sha256:836a778de4fec4dcd1dcd89ed8abff8a221f58308462e1c4aa2a3cf30148f0b9", size = 44707, upload-time = "2023-09-30T13:58:05.479Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f1/7b/ce1eafaf1a76852e2ec9b22edecf1daa58175c090266e9f6c64afcd81d91/stack_data-0.6.3-py3-none-any.whl", hash = "sha256:d5558e0c25a4cb0853cddad3d77da9891a08cb85dd9f9f91b9f8cd66e511e695", size = 24521, upload-time = "2023-09-30T13:58:03.53Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/d9/3dc2289e1f3b32eb19b9785b6a006b28ee99acb37d1d47f78d4c10e28bf8/tomli-2.4.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b5ef256a3fd497d4973c11bf142e9ed78b150d36f5773f1ca6088c230ffc5867", size = 153663, upload-time = "2026-01-11T11:21:45.27Z" }, + { url = "https://files.pythonhosted.org/packages/51/32/ef9f6845e6b9ca392cd3f64f9ec185cc6f09f0a2df3db08cbe8809d1d435/tomli-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5572e41282d5268eb09a697c89a7bee84fae66511f87533a6f88bd2f7b652da9", size = 148469, upload-time = "2026-01-11T11:21:46.873Z" }, + { url = "https://files.pythonhosted.org/packages/d6/c2/506e44cce89a8b1b1e047d64bd495c22c9f71f21e05f380f1a950dd9c217/tomli-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:551e321c6ba03b55676970b47cb1b73f14a0a4dce6a3e1a9458fd6d921d72e95", size = 236039, upload-time = "2026-01-11T11:21:48.503Z" }, + { url = "https://files.pythonhosted.org/packages/b3/40/e1b65986dbc861b7e986e8ec394598187fa8aee85b1650b01dd925ca0be8/tomli-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5e3f639a7a8f10069d0e15408c0b96a2a828cfdec6fca05296ebcdcc28ca7c76", size = 243007, upload-time = "2026-01-11T11:21:49.456Z" }, + { url = "https://files.pythonhosted.org/packages/9c/6f/6e39ce66b58a5b7ae572a0f4352ff40c71e8573633deda43f6a379d56b3e/tomli-2.4.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:1b168f2731796b045128c45982d3a4874057626da0e2ef1fdd722848b741361d", size = 240875, upload-time = "2026-01-11T11:21:50.755Z" }, + { url = "https://files.pythonhosted.org/packages/aa/ad/cb089cb190487caa80204d503c7fd0f4d443f90b95cf4ef5cf5aa0f439b0/tomli-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:133e93646ec4300d651839d382d63edff11d8978be23da4cc106f5a18b7d0576", size = 246271, upload-time = "2026-01-11T11:21:51.81Z" }, + { url = "https://files.pythonhosted.org/packages/0b/63/69125220e47fd7a3a27fd0de0c6398c89432fec41bc739823bcc66506af6/tomli-2.4.0-cp311-cp311-win32.whl", hash = "sha256:b6c78bdf37764092d369722d9946cb65b8767bfa4110f902a1b2542d8d173c8a", size = 96770, upload-time = "2026-01-11T11:21:52.647Z" }, + { url = "https://files.pythonhosted.org/packages/1e/0d/a22bb6c83f83386b0008425a6cd1fa1c14b5f3dd4bad05e98cf3dbbf4a64/tomli-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:d3d1654e11d724760cdb37a3d7691f0be9db5fbdaef59c9f532aabf87006dbaa", size = 107626, upload-time = "2026-01-11T11:21:53.459Z" }, + { url = "https://files.pythonhosted.org/packages/2f/6d/77be674a3485e75cacbf2ddba2b146911477bd887dda9d8c9dfb2f15e871/tomli-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:cae9c19ed12d4e8f3ebf46d1a75090e4c0dc16271c5bce1c833ac168f08fb614", size = 94842, upload-time = "2026-01-11T11:21:54.831Z" }, + { url = "https://files.pythonhosted.org/packages/3c/43/7389a1869f2f26dba52404e1ef13b4784b6b37dac93bac53457e3ff24ca3/tomli-2.4.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:920b1de295e72887bafa3ad9f7a792f811847d57ea6b1215154030cf131f16b1", size = 154894, upload-time = "2026-01-11T11:21:56.07Z" }, + { url = "https://files.pythonhosted.org/packages/e9/05/2f9bf110b5294132b2edf13fe6ca6ae456204f3d749f623307cbb7a946f2/tomli-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:7d6d9a4aee98fac3eab4952ad1d73aee87359452d1c086b5ceb43ed02ddb16b8", size = 149053, upload-time = "2026-01-11T11:21:57.467Z" }, + { url = "https://files.pythonhosted.org/packages/e8/41/1eda3ca1abc6f6154a8db4d714a4d35c4ad90adc0bcf700657291593fbf3/tomli-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:36b9d05b51e65b254ea6c2585b59d2c4cb91c8a3d91d0ed0f17591a29aaea54a", size = 243481, upload-time = "2026-01-11T11:21:58.661Z" }, + { url = "https://files.pythonhosted.org/packages/d2/6d/02ff5ab6c8868b41e7d4b987ce2b5f6a51d3335a70aa144edd999e055a01/tomli-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1c8a885b370751837c029ef9bc014f27d80840e48bac415f3412e6593bbc18c1", size = 251720, upload-time = "2026-01-11T11:22:00.178Z" }, + { url = "https://files.pythonhosted.org/packages/7b/57/0405c59a909c45d5b6f146107c6d997825aa87568b042042f7a9c0afed34/tomli-2.4.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8768715ffc41f0008abe25d808c20c3d990f42b6e2e58305d5da280ae7d1fa3b", size = 247014, upload-time = "2026-01-11T11:22:01.238Z" }, + { url = "https://files.pythonhosted.org/packages/2c/0e/2e37568edd944b4165735687cbaf2fe3648129e440c26d02223672ee0630/tomli-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:7b438885858efd5be02a9a133caf5812b8776ee0c969fea02c45e8e3f296ba51", size = 251820, upload-time = "2026-01-11T11:22:02.727Z" }, + { url = "https://files.pythonhosted.org/packages/5a/1c/ee3b707fdac82aeeb92d1a113f803cf6d0f37bdca0849cb489553e1f417a/tomli-2.4.0-cp312-cp312-win32.whl", hash = "sha256:0408e3de5ec77cc7f81960c362543cbbd91ef883e3138e81b729fc3eea5b9729", size = 97712, upload-time = "2026-01-11T11:22:03.777Z" }, + { url = "https://files.pythonhosted.org/packages/69/13/c07a9177d0b3bab7913299b9278845fc6eaaca14a02667c6be0b0a2270c8/tomli-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:685306e2cc7da35be4ee914fd34ab801a6acacb061b6a7abca922aaf9ad368da", size = 108296, upload-time = "2026-01-11T11:22:04.86Z" }, + { url = "https://files.pythonhosted.org/packages/18/27/e267a60bbeeee343bcc279bb9e8fbed0cbe224bc7b2a3dc2975f22809a09/tomli-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:5aa48d7c2356055feef06a43611fc401a07337d5b006be13a30f6c58f869e3c3", size = 94553, upload-time = "2026-01-11T11:22:05.854Z" }, + { url = "https://files.pythonhosted.org/packages/34/91/7f65f9809f2936e1f4ce6268ae1903074563603b2a2bd969ebbda802744f/tomli-2.4.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:84d081fbc252d1b6a982e1870660e7330fb8f90f676f6e78b052ad4e64714bf0", size = 154915, upload-time = "2026-01-11T11:22:06.703Z" }, + { url = "https://files.pythonhosted.org/packages/20/aa/64dd73a5a849c2e8f216b755599c511badde80e91e9bc2271baa7b2cdbb1/tomli-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:9a08144fa4cba33db5255f9b74f0b89888622109bd2776148f2597447f92a94e", size = 149038, upload-time = "2026-01-11T11:22:07.56Z" }, + { url = "https://files.pythonhosted.org/packages/9e/8a/6d38870bd3d52c8d1505ce054469a73f73a0fe62c0eaf5dddf61447e32fa/tomli-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c73add4bb52a206fd0c0723432db123c0c75c280cbd67174dd9d2db228ebb1b4", size = 242245, upload-time = "2026-01-11T11:22:08.344Z" }, + { url = "https://files.pythonhosted.org/packages/59/bb/8002fadefb64ab2669e5b977df3f5e444febea60e717e755b38bb7c41029/tomli-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb2945cbe303b1419e2706e711b7113da57b7db31ee378d08712d678a34e51e", size = 250335, upload-time = "2026-01-11T11:22:09.951Z" }, + { url = "https://files.pythonhosted.org/packages/a5/3d/4cdb6f791682b2ea916af2de96121b3cb1284d7c203d97d92d6003e91c8d/tomli-2.4.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bbb1b10aa643d973366dc2cb1ad94f99c1726a02343d43cbc011edbfac579e7c", size = 245962, upload-time = "2026-01-11T11:22:11.27Z" }, + { url = "https://files.pythonhosted.org/packages/f2/4a/5f25789f9a460bd858ba9756ff52d0830d825b458e13f754952dd15fb7bb/tomli-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4cbcb367d44a1f0c2be408758b43e1ffb5308abe0ea222897d6bfc8e8281ef2f", size = 250396, upload-time = "2026-01-11T11:22:12.325Z" }, + { url = "https://files.pythonhosted.org/packages/aa/2f/b73a36fea58dfa08e8b3a268750e6853a6aac2a349241a905ebd86f3047a/tomli-2.4.0-cp313-cp313-win32.whl", hash = "sha256:7d49c66a7d5e56ac959cb6fc583aff0651094ec071ba9ad43df785abc2320d86", size = 97530, upload-time = "2026-01-11T11:22:13.865Z" }, + { url = "https://files.pythonhosted.org/packages/3b/af/ca18c134b5d75de7e8dc551c5234eaba2e8e951f6b30139599b53de9c187/tomli-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:3cf226acb51d8f1c394c1b310e0e0e61fecdd7adcb78d01e294ac297dd2e7f87", size = 108227, upload-time = "2026-01-11T11:22:15.224Z" }, + { url = "https://files.pythonhosted.org/packages/22/c3/b386b832f209fee8073c8138ec50f27b4460db2fdae9ffe022df89a57f9b/tomli-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:d20b797a5c1ad80c516e41bc1fb0443ddb5006e9aaa7bda2d71978346aeb9132", size = 94748, upload-time = "2026-01-11T11:22:16.009Z" }, + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "traitlets" +version = "5.14.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/eb/79/72064e6a701c2183016abbbfedaba506d81e30e232a68c9f0d6f6fcd1574/traitlets-5.14.3.tar.gz", hash = "sha256:9ed0579d3502c94b4b3732ac120375cda96f923114522847de4b3bb98b96b6b7", size = 161621, upload-time = "2024-04-19T11:11:49.746Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" }, +] + +[[package]] +name = "twine" +version = "6.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "id" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "keyring", marker = "platform_machine != 'ppc64le' and platform_machine != 's390x'" }, + { name = "packaging" }, + { name = "readme-renderer" }, + { name = "requests" }, + { name = "requests-toolbelt" }, + { name = "rfc3986" }, + { name = "rich" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e0/a8/949edebe3a82774c1ec34f637f5dd82d1cf22c25e963b7d63771083bbee5/twine-6.2.0.tar.gz", hash = "sha256:e5ed0d2fd70c9959770dce51c8f39c8945c574e18173a7b81802dab51b4b75cf", size = 172262, upload-time = "2025-09-04T15:43:17.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/7a/882d99539b19b1490cac5d77c67338d126e4122c8276bf640e411650c830/twine-6.2.0-py3-none-any.whl", hash = "sha256:418ebf08ccda9a8caaebe414433b0ba5e25eb5e4a927667122fbe8f829f985d8", size = 42727, upload-time = "2025-09-04T15:43:15.994Z" }, +] + +[[package]] +name = "types-aiofiles" +version = "25.1.0.20251011" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/84/6c/6d23908a8217e36704aa9c79d99a620f2fdd388b66a4b7f72fbc6b6ff6c6/types_aiofiles-25.1.0.20251011.tar.gz", hash = "sha256:1c2b8ab260cb3cd40c15f9d10efdc05a6e1e6b02899304d80dfa0410e028d3ff", size = 14535, upload-time = "2025-10-11T02:44:51.237Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/71/0f/76917bab27e270bb6c32addd5968d69e558e5b6f7fb4ac4cbfa282996a96/types_aiofiles-25.1.0.20251011-py3-none-any.whl", hash = "sha256:8ff8de7f9d42739d8f0dadcceeb781ce27cd8d8c4152d4a7c52f6b20edb8149c", size = 14338, upload-time = "2025-10-11T02:44:50.054Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.32.1.20251210" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version < '3.10'", +] +sdist = { url = "https://files.pythonhosted.org/packages/c2/59/c743a842911887cd96d56aa8936522b0cd5f7a7f228c96e81b59fced45be/types_protobuf-6.32.1.20251210.tar.gz", hash = "sha256:c698bb3f020274b1a2798ae09dc773728ce3f75209a35187bd11916ebfde6763", size = 63900, upload-time = "2025-12-10T03:14:25.451Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/aa/43/58e75bac4219cbafee83179505ff44cae3153ec279be0e30583a73b8f108/types_protobuf-6.32.1.20251210-py3-none-any.whl", hash = "sha256:2641f78f3696822a048cfb8d0ff42ccd85c25f12f871fbebe86da63793692140", size = 77921, upload-time = "2025-12-10T03:14:24.477Z" }, +] + +[[package]] +name = "types-protobuf" +version = "6.32.1.20260221" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/e2/9aa4a3b2469508bd7b4e2ae11cbedaf419222a09a1b94daffcd5efca4023/types_protobuf-6.32.1.20260221.tar.gz", hash = "sha256:6d5fb060a616bfb076cbb61b4b3c3969f5fc8bec5810f9a2f7e648ee5cbcbf6e", size = 64408, upload-time = "2026-02-21T03:55:13.916Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/e8/1fd38926f9cf031188fbc5a96694203ea6f24b0e34bd64a225ec6f6291ba/types_protobuf-6.32.1.20260221-py3-none-any.whl", hash = "sha256:da7cdd947975964a93c30bfbcc2c6841ee646b318d3816b033adc2c4eb6448e4", size = 77956, upload-time = "2026-02-21T03:55:12.894Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "typing-inspection" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/35/a2/8e3becb46433538a38726c948d3399905a4c7cabd0df578ede5dc51f0ec2/wcwidth-0.6.0.tar.gz", hash = "sha256:cdc4e4262d6ef9a1a57e018384cbeb1208d8abbc64176027e2c2455c81313159", size = 159684, upload-time = "2026-02-06T19:19:40.919Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/68/5a/199c59e0a824a3db2b89c5d2dade7ab5f9624dbf6448dc291b46d5ec94d3/wcwidth-0.6.0-py3-none-any.whl", hash = "sha256:1a3a1e510b553315f8e146c54764f4fb6264ffad731b3d78088cdb1478ffbdad", size = 94189, upload-time = "2026-02-06T19:19:39.646Z" }, +] + +[[package]] +name = "wheel" +version = "0.46.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "packaging" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/89/24/a2eb353a6edac9a0303977c4cb048134959dd2a51b48a269dfc9dde00c8a/wheel-0.46.3.tar.gz", hash = "sha256:e3e79874b07d776c40bd6033f8ddf76a7dad46a7b8aa1b2787a83083519a1803", size = 60605, upload-time = "2026-01-22T12:39:49.136Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/22/b76d483683216dde3d67cba61fb2444be8d5be289bf628c13fc0fd90e5f9/wheel-0.46.3-py3-none-any.whl", hash = "sha256:4b399d56c9d9338230118d705d9737a2a468ccca63d5e813e2a4fc7815d8bc4d", size = 30557, upload-time = "2026-01-22T12:39:48.099Z" }, +] + +[[package]] +name = "yarl" +version = "1.22.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "idna" }, + { name = "multidict" }, + { name = "propcache" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/63/0c6ebca57330cd313f6102b16dd57ffaf3ec4c83403dcb45dbd15c6f3ea1/yarl-1.22.0.tar.gz", hash = "sha256:bebf8557577d4401ba8bd9ff33906f1376c877aa78d1fe216ad01b4d6745af71", size = 187169, upload-time = "2025-10-06T14:12:55.963Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/43/a2204825342f37c337f5edb6637040fa14e365b2fcc2346960201d457579/yarl-1.22.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:c7bd6683587567e5a49ee6e336e0612bec8329be1b7d4c8af5687dcdeb67ee1e", size = 140517, upload-time = "2025-10-06T14:08:42.494Z" }, + { url = "https://files.pythonhosted.org/packages/44/6f/674f3e6f02266428c56f704cd2501c22f78e8b2eeb23f153117cc86fb28a/yarl-1.22.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5cdac20da754f3a723cceea5b3448e1a2074866406adeb4ef35b469d089adb8f", size = 93495, upload-time = "2025-10-06T14:08:46.2Z" }, + { url = "https://files.pythonhosted.org/packages/b8/12/5b274d8a0f30c07b91b2f02cba69152600b47830fcfb465c108880fcee9c/yarl-1.22.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:07a524d84df0c10f41e3ee918846e1974aba4ec017f990dc735aad487a0bdfdf", size = 94400, upload-time = "2025-10-06T14:08:47.855Z" }, + { url = "https://files.pythonhosted.org/packages/e2/7f/df1b6949b1fa1aa9ff6de6e2631876ad4b73c4437822026e85d8acb56bb1/yarl-1.22.0-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1b329cb8146d7b736677a2440e422eadd775d1806a81db2d4cded80a48efc1a", size = 347545, upload-time = "2025-10-06T14:08:49.683Z" }, + { url = "https://files.pythonhosted.org/packages/84/09/f92ed93bd6cd77872ab6c3462df45ca45cd058d8f1d0c9b4f54c1704429f/yarl-1.22.0-cp310-cp310-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:75976c6945d85dbb9ee6308cd7ff7b1fb9409380c82d6119bd778d8fcfe2931c", size = 319598, upload-time = "2025-10-06T14:08:51.215Z" }, + { url = "https://files.pythonhosted.org/packages/c3/97/ac3f3feae7d522cf7ccec3d340bb0b2b61c56cb9767923df62a135092c6b/yarl-1.22.0-cp310-cp310-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:80ddf7a5f8c86cb3eb4bc9028b07bbbf1f08a96c5c0bc1244be5e8fefcb94147", size = 363893, upload-time = "2025-10-06T14:08:53.144Z" }, + { url = "https://files.pythonhosted.org/packages/06/49/f3219097403b9c84a4d079b1d7bda62dd9b86d0d6e4428c02d46ab2c77fc/yarl-1.22.0-cp310-cp310-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d332fc2e3c94dad927f2112395772a4e4fedbcf8f80efc21ed7cdfae4d574fdb", size = 371240, upload-time = "2025-10-06T14:08:55.036Z" }, + { url = "https://files.pythonhosted.org/packages/35/9f/06b765d45c0e44e8ecf0fe15c9eacbbde342bb5b7561c46944f107bfb6c3/yarl-1.22.0-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0cf71bf877efeac18b38d3930594c0948c82b64547c1cf420ba48722fe5509f6", size = 346965, upload-time = "2025-10-06T14:08:56.722Z" }, + { url = "https://files.pythonhosted.org/packages/c5/69/599e7cea8d0fcb1694323b0db0dda317fa3162f7b90166faddecf532166f/yarl-1.22.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:663e1cadaddae26be034a6ab6072449a8426ddb03d500f43daf952b74553bba0", size = 342026, upload-time = "2025-10-06T14:08:58.563Z" }, + { url = "https://files.pythonhosted.org/packages/95/6f/9dfd12c8bc90fea9eab39832ee32ea48f8e53d1256252a77b710c065c89f/yarl-1.22.0-cp310-cp310-musllinux_1_2_armv7l.whl", hash = "sha256:6dcbb0829c671f305be48a7227918cfcd11276c2d637a8033a99a02b67bf9eda", size = 335637, upload-time = "2025-10-06T14:09:00.506Z" }, + { url = "https://files.pythonhosted.org/packages/57/2e/34c5b4eb9b07e16e873db5b182c71e5f06f9b5af388cdaa97736d79dd9a6/yarl-1.22.0-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:f0d97c18dfd9a9af4490631905a3f131a8e4c9e80a39353919e2cfed8f00aedc", size = 359082, upload-time = "2025-10-06T14:09:01.936Z" }, + { url = "https://files.pythonhosted.org/packages/31/71/fa7e10fb772d273aa1f096ecb8ab8594117822f683bab7d2c5a89914c92a/yarl-1.22.0-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:437840083abe022c978470b942ff832c3940b2ad3734d424b7eaffcd07f76737", size = 357811, upload-time = "2025-10-06T14:09:03.445Z" }, + { url = "https://files.pythonhosted.org/packages/26/da/11374c04e8e1184a6a03cf9c8f5688d3e5cec83ed6f31ad3481b3207f709/yarl-1.22.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:a899cbd98dce6f5d8de1aad31cb712ec0a530abc0a86bd6edaa47c1090138467", size = 351223, upload-time = "2025-10-06T14:09:05.401Z" }, + { url = "https://files.pythonhosted.org/packages/82/8f/e2d01f161b0c034a30410e375e191a5d27608c1f8693bab1a08b089ca096/yarl-1.22.0-cp310-cp310-win32.whl", hash = "sha256:595697f68bd1f0c1c159fcb97b661fc9c3f5db46498043555d04805430e79bea", size = 82118, upload-time = "2025-10-06T14:09:11.148Z" }, + { url = "https://files.pythonhosted.org/packages/62/46/94c76196642dbeae634c7a61ba3da88cd77bed875bf6e4a8bed037505aa6/yarl-1.22.0-cp310-cp310-win_amd64.whl", hash = "sha256:cb95a9b1adaa48e41815a55ae740cfda005758104049a640a398120bf02515ca", size = 86852, upload-time = "2025-10-06T14:09:12.958Z" }, + { url = "https://files.pythonhosted.org/packages/af/af/7df4f179d3b1a6dcb9a4bd2ffbc67642746fcafdb62580e66876ce83fff4/yarl-1.22.0-cp310-cp310-win_arm64.whl", hash = "sha256:b85b982afde6df99ecc996990d4ad7ccbdbb70e2a4ba4de0aecde5922ba98a0b", size = 82012, upload-time = "2025-10-06T14:09:14.664Z" }, + { url = "https://files.pythonhosted.org/packages/4d/27/5ab13fc84c76a0250afd3d26d5936349a35be56ce5785447d6c423b26d92/yarl-1.22.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1ab72135b1f2db3fed3997d7e7dc1b80573c67138023852b6efb336a5eae6511", size = 141607, upload-time = "2025-10-06T14:09:16.298Z" }, + { url = "https://files.pythonhosted.org/packages/6a/a1/d065d51d02dc02ce81501d476b9ed2229d9a990818332242a882d5d60340/yarl-1.22.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:669930400e375570189492dc8d8341301578e8493aec04aebc20d4717f899dd6", size = 94027, upload-time = "2025-10-06T14:09:17.786Z" }, + { url = "https://files.pythonhosted.org/packages/c1/da/8da9f6a53f67b5106ffe902c6fa0164e10398d4e150d85838b82f424072a/yarl-1.22.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:792a2af6d58177ef7c19cbf0097aba92ca1b9cb3ffdd9c7470e156c8f9b5e028", size = 94963, upload-time = "2025-10-06T14:09:19.662Z" }, + { url = "https://files.pythonhosted.org/packages/68/fe/2c1f674960c376e29cb0bec1249b117d11738db92a6ccc4a530b972648db/yarl-1.22.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3ea66b1c11c9150f1372f69afb6b8116f2dd7286f38e14ea71a44eee9ec51b9d", size = 368406, upload-time = "2025-10-06T14:09:21.402Z" }, + { url = "https://files.pythonhosted.org/packages/95/26/812a540e1c3c6418fec60e9bbd38e871eaba9545e94fa5eff8f4a8e28e1e/yarl-1.22.0-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3e2daa88dc91870215961e96a039ec73e4937da13cf77ce17f9cad0c18df3503", size = 336581, upload-time = "2025-10-06T14:09:22.98Z" }, + { url = "https://files.pythonhosted.org/packages/0b/f5/5777b19e26fdf98563985e481f8be3d8a39f8734147a6ebf459d0dab5a6b/yarl-1.22.0-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ba440ae430c00eee41509353628600212112cd5018d5def7e9b05ea7ac34eb65", size = 388924, upload-time = "2025-10-06T14:09:24.655Z" }, + { url = "https://files.pythonhosted.org/packages/86/08/24bd2477bd59c0bbd994fe1d93b126e0472e4e3df5a96a277b0a55309e89/yarl-1.22.0-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:e6438cc8f23a9c1478633d216b16104a586b9761db62bfacb6425bac0a36679e", size = 392890, upload-time = "2025-10-06T14:09:26.617Z" }, + { url = "https://files.pythonhosted.org/packages/46/00/71b90ed48e895667ecfb1eaab27c1523ee2fa217433ed77a73b13205ca4b/yarl-1.22.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4c52a6e78aef5cf47a98ef8e934755abf53953379b7d53e68b15ff4420e6683d", size = 365819, upload-time = "2025-10-06T14:09:28.544Z" }, + { url = "https://files.pythonhosted.org/packages/30/2d/f715501cae832651d3282387c6a9236cd26bd00d0ff1e404b3dc52447884/yarl-1.22.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:3b06bcadaac49c70f4c88af4ffcfbe3dc155aab3163e75777818092478bcbbe7", size = 363601, upload-time = "2025-10-06T14:09:30.568Z" }, + { url = "https://files.pythonhosted.org/packages/f8/f9/a678c992d78e394e7126ee0b0e4e71bd2775e4334d00a9278c06a6cce96a/yarl-1.22.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:6944b2dc72c4d7f7052683487e3677456050ff77fcf5e6204e98caf785ad1967", size = 358072, upload-time = "2025-10-06T14:09:32.528Z" }, + { url = "https://files.pythonhosted.org/packages/2c/d1/b49454411a60edb6fefdcad4f8e6dbba7d8019e3a508a1c5836cba6d0781/yarl-1.22.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:d5372ca1df0f91a86b047d1277c2aaf1edb32d78bbcefffc81b40ffd18f027ed", size = 385311, upload-time = "2025-10-06T14:09:34.634Z" }, + { url = "https://files.pythonhosted.org/packages/87/e5/40d7a94debb8448c7771a916d1861d6609dddf7958dc381117e7ba36d9e8/yarl-1.22.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:51af598701f5299012b8416486b40fceef8c26fc87dc6d7d1f6fc30609ea0aa6", size = 381094, upload-time = "2025-10-06T14:09:36.268Z" }, + { url = "https://files.pythonhosted.org/packages/35/d8/611cc282502381ad855448643e1ad0538957fc82ae83dfe7762c14069e14/yarl-1.22.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b266bd01fedeffeeac01a79ae181719ff848a5a13ce10075adbefc8f1daee70e", size = 370944, upload-time = "2025-10-06T14:09:37.872Z" }, + { url = "https://files.pythonhosted.org/packages/2d/df/fadd00fb1c90e1a5a8bd731fa3d3de2e165e5a3666a095b04e31b04d9cb6/yarl-1.22.0-cp311-cp311-win32.whl", hash = "sha256:a9b1ba5610a4e20f655258d5a1fdc7ebe3d837bb0e45b581398b99eb98b1f5ca", size = 81804, upload-time = "2025-10-06T14:09:39.359Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/149bb6f45f267cb5c074ac40c01c6b3ea6d8a620d34b337f6321928a1b4d/yarl-1.22.0-cp311-cp311-win_amd64.whl", hash = "sha256:078278b9b0b11568937d9509b589ee83ef98ed6d561dfe2020e24a9fd08eaa2b", size = 86858, upload-time = "2025-10-06T14:09:41.068Z" }, + { url = "https://files.pythonhosted.org/packages/2b/13/88b78b93ad3f2f0b78e13bfaaa24d11cbc746e93fe76d8c06bf139615646/yarl-1.22.0-cp311-cp311-win_arm64.whl", hash = "sha256:b6a6f620cfe13ccec221fa312139135166e47ae169f8253f72a0abc0dae94376", size = 81637, upload-time = "2025-10-06T14:09:42.712Z" }, + { url = "https://files.pythonhosted.org/packages/75/ff/46736024fee3429b80a165a732e38e5d5a238721e634ab41b040d49f8738/yarl-1.22.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:e340382d1afa5d32b892b3ff062436d592ec3d692aeea3bef3a5cfe11bbf8c6f", size = 142000, upload-time = "2025-10-06T14:09:44.631Z" }, + { url = "https://files.pythonhosted.org/packages/5a/9a/b312ed670df903145598914770eb12de1bac44599549b3360acc96878df8/yarl-1.22.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:f1e09112a2c31ffe8d80be1b0988fa6a18c5d5cad92a9ffbb1c04c91bfe52ad2", size = 94338, upload-time = "2025-10-06T14:09:46.372Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f5/0601483296f09c3c65e303d60c070a5c19fcdbc72daa061e96170785bc7d/yarl-1.22.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:939fe60db294c786f6b7c2d2e121576628468f65453d86b0fe36cb52f987bd74", size = 94909, upload-time = "2025-10-06T14:09:48.648Z" }, + { url = "https://files.pythonhosted.org/packages/60/41/9a1fe0b73dbcefce72e46cf149b0e0a67612d60bfc90fb59c2b2efdfbd86/yarl-1.22.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1651bf8e0398574646744c1885a41198eba53dc8a9312b954073f845c90a8df", size = 372940, upload-time = "2025-10-06T14:09:50.089Z" }, + { url = "https://files.pythonhosted.org/packages/17/7a/795cb6dfee561961c30b800f0ed616b923a2ec6258b5def2a00bf8231334/yarl-1.22.0-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:b8a0588521a26bf92a57a1705b77b8b59044cdceccac7151bd8d229e66b8dedb", size = 345825, upload-time = "2025-10-06T14:09:52.142Z" }, + { url = "https://files.pythonhosted.org/packages/d7/93/a58f4d596d2be2ae7bab1a5846c4d270b894958845753b2c606d666744d3/yarl-1.22.0-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:42188e6a615c1a75bcaa6e150c3fe8f3e8680471a6b10150c5f7e83f47cc34d2", size = 386705, upload-time = "2025-10-06T14:09:54.128Z" }, + { url = "https://files.pythonhosted.org/packages/61/92/682279d0e099d0e14d7fd2e176bd04f48de1484f56546a3e1313cd6c8e7c/yarl-1.22.0-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:f6d2cb59377d99718913ad9a151030d6f83ef420a2b8f521d94609ecc106ee82", size = 396518, upload-time = "2025-10-06T14:09:55.762Z" }, + { url = "https://files.pythonhosted.org/packages/db/0f/0d52c98b8a885aeda831224b78f3be7ec2e1aa4a62091f9f9188c3c65b56/yarl-1.22.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:50678a3b71c751d58d7908edc96d332af328839eea883bb554a43f539101277a", size = 377267, upload-time = "2025-10-06T14:09:57.958Z" }, + { url = "https://files.pythonhosted.org/packages/22/42/d2685e35908cbeaa6532c1fc73e89e7f2efb5d8a7df3959ea8e37177c5a3/yarl-1.22.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:1e8fbaa7cec507aa24ea27a01456e8dd4b6fab829059b69844bd348f2d467124", size = 365797, upload-time = "2025-10-06T14:09:59.527Z" }, + { url = "https://files.pythonhosted.org/packages/a2/83/cf8c7bcc6355631762f7d8bdab920ad09b82efa6b722999dfb05afa6cfac/yarl-1.22.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:433885ab5431bc3d3d4f2f9bd15bfa1614c522b0f1405d62c4f926ccd69d04fa", size = 365535, upload-time = "2025-10-06T14:10:01.139Z" }, + { url = "https://files.pythonhosted.org/packages/25/e1/5302ff9b28f0c59cac913b91fe3f16c59a033887e57ce9ca5d41a3a94737/yarl-1.22.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:b790b39c7e9a4192dc2e201a282109ed2985a1ddbd5ac08dc56d0e121400a8f7", size = 382324, upload-time = "2025-10-06T14:10:02.756Z" }, + { url = "https://files.pythonhosted.org/packages/bf/cd/4617eb60f032f19ae3a688dc990d8f0d89ee0ea378b61cac81ede3e52fae/yarl-1.22.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:31f0b53913220599446872d757257be5898019c85e7971599065bc55065dc99d", size = 383803, upload-time = "2025-10-06T14:10:04.552Z" }, + { url = "https://files.pythonhosted.org/packages/59/65/afc6e62bb506a319ea67b694551dab4a7e6fb7bf604e9bd9f3e11d575fec/yarl-1.22.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:a49370e8f711daec68d09b821a34e1167792ee2d24d405cbc2387be4f158b520", size = 374220, upload-time = "2025-10-06T14:10:06.489Z" }, + { url = "https://files.pythonhosted.org/packages/e7/3d/68bf18d50dc674b942daec86a9ba922d3113d8399b0e52b9897530442da2/yarl-1.22.0-cp312-cp312-win32.whl", hash = "sha256:70dfd4f241c04bd9239d53b17f11e6ab672b9f1420364af63e8531198e3f5fe8", size = 81589, upload-time = "2025-10-06T14:10:09.254Z" }, + { url = "https://files.pythonhosted.org/packages/c8/9a/6ad1a9b37c2f72874f93e691b2e7ecb6137fb2b899983125db4204e47575/yarl-1.22.0-cp312-cp312-win_amd64.whl", hash = "sha256:8884d8b332a5e9b88e23f60bb166890009429391864c685e17bd73a9eda9105c", size = 87213, upload-time = "2025-10-06T14:10:11.369Z" }, + { url = "https://files.pythonhosted.org/packages/44/c5/c21b562d1680a77634d748e30c653c3ca918beb35555cff24986fff54598/yarl-1.22.0-cp312-cp312-win_arm64.whl", hash = "sha256:ea70f61a47f3cc93bdf8b2f368ed359ef02a01ca6393916bc8ff877427181e74", size = 81330, upload-time = "2025-10-06T14:10:13.112Z" }, + { url = "https://files.pythonhosted.org/packages/ea/f3/d67de7260456ee105dc1d162d43a019ecad6b91e2f51809d6cddaa56690e/yarl-1.22.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:8dee9c25c74997f6a750cd317b8ca63545169c098faee42c84aa5e506c819b53", size = 139980, upload-time = "2025-10-06T14:10:14.601Z" }, + { url = "https://files.pythonhosted.org/packages/01/88/04d98af0b47e0ef42597b9b28863b9060bb515524da0a65d5f4db160b2d5/yarl-1.22.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:01e73b85a5434f89fc4fe27dcda2aff08ddf35e4d47bbbea3bdcd25321af538a", size = 93424, upload-time = "2025-10-06T14:10:16.115Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/3274b215fd8442a03975ce6bee5fe6aa57a8326b29b9d3d56234a1dca244/yarl-1.22.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:22965c2af250d20c873cdbee8ff958fb809940aeb2e74ba5f20aaf6b7ac8c70c", size = 93821, upload-time = "2025-10-06T14:10:17.993Z" }, + { url = "https://files.pythonhosted.org/packages/61/3a/caf4e25036db0f2da4ca22a353dfeb3c9d3c95d2761ebe9b14df8fc16eb0/yarl-1.22.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b4f15793aa49793ec8d1c708ab7f9eded1aa72edc5174cae703651555ed1b601", size = 373243, upload-time = "2025-10-06T14:10:19.44Z" }, + { url = "https://files.pythonhosted.org/packages/6e/9e/51a77ac7516e8e7803b06e01f74e78649c24ee1021eca3d6a739cb6ea49c/yarl-1.22.0-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e5542339dcf2747135c5c85f68680353d5cb9ffd741c0f2e8d832d054d41f35a", size = 342361, upload-time = "2025-10-06T14:10:21.124Z" }, + { url = "https://files.pythonhosted.org/packages/d4/f8/33b92454789dde8407f156c00303e9a891f1f51a0330b0fad7c909f87692/yarl-1.22.0-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:5c401e05ad47a75869c3ab3e35137f8468b846770587e70d71e11de797d113df", size = 387036, upload-time = "2025-10-06T14:10:22.902Z" }, + { url = "https://files.pythonhosted.org/packages/d9/9a/c5db84ea024f76838220280f732970aa4ee154015d7f5c1bfb60a267af6f/yarl-1.22.0-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:243dda95d901c733f5b59214d28b0120893d91777cb8aa043e6ef059d3cddfe2", size = 397671, upload-time = "2025-10-06T14:10:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/11/c9/cd8538dc2e7727095e0c1d867bad1e40c98f37763e6d995c1939f5fdc7b1/yarl-1.22.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bec03d0d388060058f5d291a813f21c011041938a441c593374da6077fe21b1b", size = 377059, upload-time = "2025-10-06T14:10:26.406Z" }, + { url = "https://files.pythonhosted.org/packages/a1/b9/ab437b261702ced75122ed78a876a6dec0a1b0f5e17a4ac7a9a2482d8abe/yarl-1.22.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:b0748275abb8c1e1e09301ee3cf90c8a99678a4e92e4373705f2a2570d581273", size = 365356, upload-time = "2025-10-06T14:10:28.461Z" }, + { url = "https://files.pythonhosted.org/packages/b2/9d/8e1ae6d1d008a9567877b08f0ce4077a29974c04c062dabdb923ed98e6fe/yarl-1.22.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:47fdb18187e2a4e18fda2c25c05d8251a9e4a521edaed757fef033e7d8498d9a", size = 361331, upload-time = "2025-10-06T14:10:30.541Z" }, + { url = "https://files.pythonhosted.org/packages/ca/5a/09b7be3905962f145b73beb468cdd53db8aa171cf18c80400a54c5b82846/yarl-1.22.0-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c7044802eec4524fde550afc28edda0dd5784c4c45f0be151a2d3ba017daca7d", size = 382590, upload-time = "2025-10-06T14:10:33.352Z" }, + { url = "https://files.pythonhosted.org/packages/aa/7f/59ec509abf90eda5048b0bc3e2d7b5099dffdb3e6b127019895ab9d5ef44/yarl-1.22.0-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:139718f35149ff544caba20fce6e8a2f71f1e39b92c700d8438a0b1d2a631a02", size = 385316, upload-time = "2025-10-06T14:10:35.034Z" }, + { url = "https://files.pythonhosted.org/packages/e5/84/891158426bc8036bfdfd862fabd0e0fa25df4176ec793e447f4b85cf1be4/yarl-1.22.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1b51bebd221006d3d2f95fbe124b22b247136647ae5dcc8c7acafba66e5ee67", size = 374431, upload-time = "2025-10-06T14:10:37.76Z" }, + { url = "https://files.pythonhosted.org/packages/bb/49/03da1580665baa8bef5e8ed34c6df2c2aca0a2f28bf397ed238cc1bbc6f2/yarl-1.22.0-cp313-cp313-win32.whl", hash = "sha256:d3e32536234a95f513bd374e93d717cf6b2231a791758de6c509e3653f234c95", size = 81555, upload-time = "2025-10-06T14:10:39.649Z" }, + { url = "https://files.pythonhosted.org/packages/9a/ee/450914ae11b419eadd067c6183ae08381cfdfcb9798b90b2b713bbebddda/yarl-1.22.0-cp313-cp313-win_amd64.whl", hash = "sha256:47743b82b76d89a1d20b83e60d5c20314cbd5ba2befc9cda8f28300c4a08ed4d", size = 86965, upload-time = "2025-10-06T14:10:41.313Z" }, + { url = "https://files.pythonhosted.org/packages/98/4d/264a01eae03b6cf629ad69bae94e3b0e5344741e929073678e84bf7a3e3b/yarl-1.22.0-cp313-cp313-win_arm64.whl", hash = "sha256:5d0fcda9608875f7d052eff120c7a5da474a6796fe4d83e152e0e4d42f6d1a9b", size = 81205, upload-time = "2025-10-06T14:10:43.167Z" }, + { url = "https://files.pythonhosted.org/packages/88/fc/6908f062a2f77b5f9f6d69cecb1747260831ff206adcbc5b510aff88df91/yarl-1.22.0-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:719ae08b6972befcba4310e49edb1161a88cdd331e3a694b84466bd938a6ab10", size = 146209, upload-time = "2025-10-06T14:10:44.643Z" }, + { url = "https://files.pythonhosted.org/packages/65/47/76594ae8eab26210b4867be6f49129861ad33da1f1ebdf7051e98492bf62/yarl-1.22.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:47d8a5c446df1c4db9d21b49619ffdba90e77c89ec6e283f453856c74b50b9e3", size = 95966, upload-time = "2025-10-06T14:10:46.554Z" }, + { url = "https://files.pythonhosted.org/packages/ab/ce/05e9828a49271ba6b5b038b15b3934e996980dd78abdfeb52a04cfb9467e/yarl-1.22.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:cfebc0ac8333520d2d0423cbbe43ae43c8838862ddb898f5ca68565e395516e9", size = 97312, upload-time = "2025-10-06T14:10:48.007Z" }, + { url = "https://files.pythonhosted.org/packages/d1/c5/7dffad5e4f2265b29c9d7ec869c369e4223166e4f9206fc2243ee9eea727/yarl-1.22.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4398557cbf484207df000309235979c79c4356518fd5c99158c7d38203c4da4f", size = 361967, upload-time = "2025-10-06T14:10:49.997Z" }, + { url = "https://files.pythonhosted.org/packages/50/b2/375b933c93a54bff7fc041e1a6ad2c0f6f733ffb0c6e642ce56ee3b39970/yarl-1.22.0-cp313-cp313t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:2ca6fd72a8cd803be290d42f2dec5cdcd5299eeb93c2d929bf060ad9efaf5de0", size = 323949, upload-time = "2025-10-06T14:10:52.004Z" }, + { url = "https://files.pythonhosted.org/packages/66/50/bfc2a29a1d78644c5a7220ce2f304f38248dc94124a326794e677634b6cf/yarl-1.22.0-cp313-cp313t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:ca1f59c4e1ab6e72f0a23c13fca5430f889634166be85dbf1013683e49e3278e", size = 361818, upload-time = "2025-10-06T14:10:54.078Z" }, + { url = "https://files.pythonhosted.org/packages/46/96/f3941a46af7d5d0f0498f86d71275696800ddcdd20426298e572b19b91ff/yarl-1.22.0-cp313-cp313t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6c5010a52015e7c70f86eb967db0f37f3c8bd503a695a49f8d45700144667708", size = 372626, upload-time = "2025-10-06T14:10:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/c1/42/8b27c83bb875cd89448e42cd627e0fb971fa1675c9ec546393d18826cb50/yarl-1.22.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9d7672ecf7557476642c88497c2f8d8542f8e36596e928e9bcba0e42e1e7d71f", size = 341129, upload-time = "2025-10-06T14:10:57.985Z" }, + { url = "https://files.pythonhosted.org/packages/49/36/99ca3122201b382a3cf7cc937b95235b0ac944f7e9f2d5331d50821ed352/yarl-1.22.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:3b7c88eeef021579d600e50363e0b6ee4f7f6f728cd3486b9d0f3ee7b946398d", size = 346776, upload-time = "2025-10-06T14:10:59.633Z" }, + { url = "https://files.pythonhosted.org/packages/85/b4/47328bf996acd01a4c16ef9dcd2f59c969f495073616586f78cd5f2efb99/yarl-1.22.0-cp313-cp313t-musllinux_1_2_armv7l.whl", hash = "sha256:f4afb5c34f2c6fecdcc182dfcfc6af6cccf1aa923eed4d6a12e9d96904e1a0d8", size = 334879, upload-time = "2025-10-06T14:11:01.454Z" }, + { url = "https://files.pythonhosted.org/packages/c2/ad/b77d7b3f14a4283bffb8e92c6026496f6de49751c2f97d4352242bba3990/yarl-1.22.0-cp313-cp313t-musllinux_1_2_ppc64le.whl", hash = "sha256:59c189e3e99a59cf8d83cbb31d4db02d66cda5a1a4374e8a012b51255341abf5", size = 350996, upload-time = "2025-10-06T14:11:03.452Z" }, + { url = "https://files.pythonhosted.org/packages/81/c8/06e1d69295792ba54d556f06686cbd6a7ce39c22307100e3fb4a2c0b0a1d/yarl-1.22.0-cp313-cp313t-musllinux_1_2_s390x.whl", hash = "sha256:5a3bf7f62a289fa90f1990422dc8dff5a458469ea71d1624585ec3a4c8d6960f", size = 356047, upload-time = "2025-10-06T14:11:05.115Z" }, + { url = "https://files.pythonhosted.org/packages/4b/b8/4c0e9e9f597074b208d18cef227d83aac36184bfbc6eab204ea55783dbc5/yarl-1.22.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:de6b9a04c606978fdfe72666fa216ffcf2d1a9f6a381058d4378f8d7b1e5de62", size = 342947, upload-time = "2025-10-06T14:11:08.137Z" }, + { url = "https://files.pythonhosted.org/packages/e0/e5/11f140a58bf4c6ad7aca69a892bff0ee638c31bea4206748fc0df4ebcb3a/yarl-1.22.0-cp313-cp313t-win32.whl", hash = "sha256:1834bb90991cc2999f10f97f5f01317f99b143284766d197e43cd5b45eb18d03", size = 86943, upload-time = "2025-10-06T14:11:10.284Z" }, + { url = "https://files.pythonhosted.org/packages/31/74/8b74bae38ed7fe6793d0c15a0c8207bbb819cf287788459e5ed230996cdd/yarl-1.22.0-cp313-cp313t-win_amd64.whl", hash = "sha256:ff86011bd159a9d2dfc89c34cfd8aff12875980e3bd6a39ff097887520e60249", size = 93715, upload-time = "2025-10-06T14:11:11.739Z" }, + { url = "https://files.pythonhosted.org/packages/69/66/991858aa4b5892d57aef7ee1ba6b4d01ec3b7eb3060795d34090a3ca3278/yarl-1.22.0-cp313-cp313t-win_arm64.whl", hash = "sha256:7861058d0582b847bc4e3a4a4c46828a410bca738673f35a29ba3ca5db0b473b", size = 83857, upload-time = "2025-10-06T14:11:13.586Z" }, + { url = "https://files.pythonhosted.org/packages/46/b3/e20ef504049f1a1c54a814b4b9bed96d1ac0e0610c3b4da178f87209db05/yarl-1.22.0-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:34b36c2c57124530884d89d50ed2c1478697ad7473efd59cfd479945c95650e4", size = 140520, upload-time = "2025-10-06T14:11:15.465Z" }, + { url = "https://files.pythonhosted.org/packages/e4/04/3532d990fdbab02e5ede063676b5c4260e7f3abea2151099c2aa745acc4c/yarl-1.22.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:0dd9a702591ca2e543631c2a017e4a547e38a5c0f29eece37d9097e04a7ac683", size = 93504, upload-time = "2025-10-06T14:11:17.106Z" }, + { url = "https://files.pythonhosted.org/packages/11/63/ff458113c5c2dac9a9719ac68ee7c947cb621432bcf28c9972b1c0e83938/yarl-1.22.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:594fcab1032e2d2cc3321bb2e51271e7cd2b516c7d9aee780ece81b07ff8244b", size = 94282, upload-time = "2025-10-06T14:11:19.064Z" }, + { url = "https://files.pythonhosted.org/packages/a7/bc/315a56aca762d44a6aaaf7ad253f04d996cb6b27bad34410f82d76ea8038/yarl-1.22.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f3d7a87a78d46a2e3d5b72587ac14b4c16952dd0887dbb051451eceac774411e", size = 372080, upload-time = "2025-10-06T14:11:20.996Z" }, + { url = "https://files.pythonhosted.org/packages/3f/3f/08e9b826ec2e099ea6e7c69a61272f4f6da62cb5b1b63590bb80ca2e4a40/yarl-1.22.0-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:852863707010316c973162e703bddabec35e8757e67fcb8ad58829de1ebc8590", size = 338696, upload-time = "2025-10-06T14:11:22.847Z" }, + { url = "https://files.pythonhosted.org/packages/e3/9f/90360108e3b32bd76789088e99538febfea24a102380ae73827f62073543/yarl-1.22.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:131a085a53bfe839a477c0845acf21efc77457ba2bcf5899618136d64f3303a2", size = 387121, upload-time = "2025-10-06T14:11:24.889Z" }, + { url = "https://files.pythonhosted.org/packages/98/92/ab8d4657bd5b46a38094cfaea498f18bb70ce6b63508fd7e909bd1f93066/yarl-1.22.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:078a8aefd263f4d4f923a9677b942b445a2be970ca24548a8102689a3a8ab8da", size = 394080, upload-time = "2025-10-06T14:11:27.307Z" }, + { url = "https://files.pythonhosted.org/packages/f5/e7/d8c5a7752fef68205296201f8ec2bf718f5c805a7a7e9880576c67600658/yarl-1.22.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bca03b91c323036913993ff5c738d0842fc9c60c4648e5c8d98331526df89784", size = 372661, upload-time = "2025-10-06T14:11:29.387Z" }, + { url = "https://files.pythonhosted.org/packages/b6/2e/f4d26183c8db0bb82d491b072f3127fb8c381a6206a3a56332714b79b751/yarl-1.22.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:68986a61557d37bb90d3051a45b91fa3d5c516d177dfc6dd6f2f436a07ff2b6b", size = 364645, upload-time = "2025-10-06T14:11:31.423Z" }, + { url = "https://files.pythonhosted.org/packages/80/7c/428e5812e6b87cd00ee8e898328a62c95825bf37c7fa87f0b6bb2ad31304/yarl-1.22.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:4792b262d585ff0dff6bcb787f8492e40698443ec982a3568c2096433660c694", size = 355361, upload-time = "2025-10-06T14:11:33.055Z" }, + { url = "https://files.pythonhosted.org/packages/ec/2a/249405fd26776f8b13c067378ef4d7dd49c9098d1b6457cdd152a99e96a9/yarl-1.22.0-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:ebd4549b108d732dba1d4ace67614b9545b21ece30937a63a65dd34efa19732d", size = 381451, upload-time = "2025-10-06T14:11:35.136Z" }, + { url = "https://files.pythonhosted.org/packages/67/a8/fb6b1adbe98cf1e2dd9fad71003d3a63a1bc22459c6e15f5714eb9323b93/yarl-1.22.0-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f87ac53513d22240c7d59203f25cc3beac1e574c6cd681bbfd321987b69f95fd", size = 383814, upload-time = "2025-10-06T14:11:37.094Z" }, + { url = "https://files.pythonhosted.org/packages/d9/f9/3aa2c0e480fb73e872ae2814c43bc1e734740bb0d54e8cb2a95925f98131/yarl-1.22.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:22b029f2881599e2f1b06f8f1db2ee63bd309e2293ba2d566e008ba12778b8da", size = 370799, upload-time = "2025-10-06T14:11:38.83Z" }, + { url = "https://files.pythonhosted.org/packages/50/3c/af9dba3b8b5eeb302f36f16f92791f3ea62e3f47763406abf6d5a4a3333b/yarl-1.22.0-cp314-cp314-win32.whl", hash = "sha256:6a635ea45ba4ea8238463b4f7d0e721bad669f80878b7bfd1f89266e2ae63da2", size = 82990, upload-time = "2025-10-06T14:11:40.624Z" }, + { url = "https://files.pythonhosted.org/packages/ac/30/ac3a0c5bdc1d6efd1b41fa24d4897a4329b3b1e98de9449679dd327af4f0/yarl-1.22.0-cp314-cp314-win_amd64.whl", hash = "sha256:0d6e6885777af0f110b0e5d7e5dda8b704efed3894da26220b7f3d887b839a79", size = 88292, upload-time = "2025-10-06T14:11:42.578Z" }, + { url = "https://files.pythonhosted.org/packages/df/0a/227ab4ff5b998a1b7410abc7b46c9b7a26b0ca9e86c34ba4b8d8bc7c63d5/yarl-1.22.0-cp314-cp314-win_arm64.whl", hash = "sha256:8218f4e98d3c10d683584cb40f0424f4b9fd6e95610232dd75e13743b070ee33", size = 82888, upload-time = "2025-10-06T14:11:44.863Z" }, + { url = "https://files.pythonhosted.org/packages/06/5e/a15eb13db90abd87dfbefb9760c0f3f257ac42a5cac7e75dbc23bed97a9f/yarl-1.22.0-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:45c2842ff0e0d1b35a6bf1cd6c690939dacb617a70827f715232b2e0494d55d1", size = 146223, upload-time = "2025-10-06T14:11:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/18/82/9665c61910d4d84f41a5bf6837597c89e665fa88aa4941080704645932a9/yarl-1.22.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:d947071e6ebcf2e2bee8fce76e10faca8f7a14808ca36a910263acaacef08eca", size = 95981, upload-time = "2025-10-06T14:11:48.845Z" }, + { url = "https://files.pythonhosted.org/packages/5d/9a/2f65743589809af4d0a6d3aa749343c4b5f4c380cc24a8e94a3c6625a808/yarl-1.22.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:334b8721303e61b00019474cc103bdac3d7b1f65e91f0bfedeec2d56dfe74b53", size = 97303, upload-time = "2025-10-06T14:11:50.897Z" }, + { url = "https://files.pythonhosted.org/packages/b0/ab/5b13d3e157505c43c3b43b5a776cbf7b24a02bc4cccc40314771197e3508/yarl-1.22.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1e7ce67c34138a058fd092f67d07a72b8e31ff0c9236e751957465a24b28910c", size = 361820, upload-time = "2025-10-06T14:11:52.549Z" }, + { url = "https://files.pythonhosted.org/packages/fb/76/242a5ef4677615cf95330cfc1b4610e78184400699bdda0acb897ef5e49a/yarl-1.22.0-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d77e1b2c6d04711478cb1c4ab90db07f1609ccf06a287d5607fcd90dc9863acf", size = 323203, upload-time = "2025-10-06T14:11:54.225Z" }, + { url = "https://files.pythonhosted.org/packages/8c/96/475509110d3f0153b43d06164cf4195c64d16999e0c7e2d8a099adcd6907/yarl-1.22.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:c4647674b6150d2cae088fc07de2738a84b8bcedebef29802cf0b0a82ab6face", size = 363173, upload-time = "2025-10-06T14:11:56.069Z" }, + { url = "https://files.pythonhosted.org/packages/c9/66/59db471aecfbd559a1fd48aedd954435558cd98c7d0da8b03cc6c140a32c/yarl-1.22.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:efb07073be061c8f79d03d04139a80ba33cbd390ca8f0297aae9cce6411e4c6b", size = 373562, upload-time = "2025-10-06T14:11:58.783Z" }, + { url = "https://files.pythonhosted.org/packages/03/1f/c5d94abc91557384719da10ff166b916107c1b45e4d0423a88457071dd88/yarl-1.22.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e51ac5435758ba97ad69617e13233da53908beccc6cfcd6c34bbed8dcbede486", size = 339828, upload-time = "2025-10-06T14:12:00.686Z" }, + { url = "https://files.pythonhosted.org/packages/5f/97/aa6a143d3afba17b6465733681c70cf175af89f76ec8d9286e08437a7454/yarl-1.22.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:33e32a0dd0c8205efa8e83d04fc9f19313772b78522d1bdc7d9aed706bfd6138", size = 347551, upload-time = "2025-10-06T14:12:02.628Z" }, + { url = "https://files.pythonhosted.org/packages/43/3c/45a2b6d80195959239a7b2a8810506d4eea5487dce61c2a3393e7fc3c52e/yarl-1.22.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:bf4a21e58b9cde0e401e683ebd00f6ed30a06d14e93f7c8fd059f8b6e8f87b6a", size = 334512, upload-time = "2025-10-06T14:12:04.871Z" }, + { url = "https://files.pythonhosted.org/packages/86/a0/c2ab48d74599c7c84cb104ebd799c5813de252bea0f360ffc29d270c2caa/yarl-1.22.0-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:e4b582bab49ac33c8deb97e058cd67c2c50dac0dd134874106d9c774fd272529", size = 352400, upload-time = "2025-10-06T14:12:06.624Z" }, + { url = "https://files.pythonhosted.org/packages/32/75/f8919b2eafc929567d3d8411f72bdb1a2109c01caaab4ebfa5f8ffadc15b/yarl-1.22.0-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:0b5bcc1a9c4839e7e30b7b30dd47fe5e7e44fb7054ec29b5bb8d526aa1041093", size = 357140, upload-time = "2025-10-06T14:12:08.362Z" }, + { url = "https://files.pythonhosted.org/packages/cf/72/6a85bba382f22cf78add705d8c3731748397d986e197e53ecc7835e76de7/yarl-1.22.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:c0232bce2170103ec23c454e54a57008a9a72b5d1c3105dc2496750da8cfa47c", size = 341473, upload-time = "2025-10-06T14:12:10.994Z" }, + { url = "https://files.pythonhosted.org/packages/35/18/55e6011f7c044dc80b98893060773cefcfdbf60dfefb8cb2f58b9bacbd83/yarl-1.22.0-cp314-cp314t-win32.whl", hash = "sha256:8009b3173bcd637be650922ac455946197d858b3630b6d8787aa9e5c4564533e", size = 89056, upload-time = "2025-10-06T14:12:13.317Z" }, + { url = "https://files.pythonhosted.org/packages/f9/86/0f0dccb6e59a9e7f122c5afd43568b1d31b8ab7dda5f1b01fb5c7025c9a9/yarl-1.22.0-cp314-cp314t-win_amd64.whl", hash = "sha256:9fb17ea16e972c63d25d4a97f016d235c78dd2344820eb35bc034bc32012ee27", size = 96292, upload-time = "2025-10-06T14:12:15.398Z" }, + { url = "https://files.pythonhosted.org/packages/48/b7/503c98092fb3b344a179579f55814b613c1fbb1c23b3ec14a7b008a66a6e/yarl-1.22.0-cp314-cp314t-win_arm64.whl", hash = "sha256:9f6d73c1436b934e3f01df1e1b21ff765cd1d28c77dfb9ace207f746d4610ee1", size = 85171, upload-time = "2025-10-06T14:12:16.935Z" }, + { url = "https://files.pythonhosted.org/packages/94/fd/6480106702a79bcceda5fd9c63cb19a04a6506bd5ce7fd8d9b63742f0021/yarl-1.22.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:3aa27acb6de7a23785d81557577491f6c38a5209a254d1191519d07d8fe51748", size = 141301, upload-time = "2025-10-06T14:12:19.01Z" }, + { url = "https://files.pythonhosted.org/packages/42/e1/6d95d21b17a93e793e4ec420a925fe1f6a9342338ca7a563ed21129c0990/yarl-1.22.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:af74f05666a5e531289cb1cc9c883d1de2088b8e5b4de48004e5ca8a830ac859", size = 93864, upload-time = "2025-10-06T14:12:21.05Z" }, + { url = "https://files.pythonhosted.org/packages/32/58/b8055273c203968e89808413ea4c984988b6649baabf10f4522e67c22d2f/yarl-1.22.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:62441e55958977b8167b2709c164c91a6363e25da322d87ae6dd9c6019ceecf9", size = 94706, upload-time = "2025-10-06T14:12:23.287Z" }, + { url = "https://files.pythonhosted.org/packages/18/91/d7bfbc28a88c2895ecd0da6a874def0c147de78afc52c773c28e1aa233a3/yarl-1.22.0-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b580e71cac3f8113d3135888770903eaf2f507e9421e5697d6ee6d8cd1c7f054", size = 347100, upload-time = "2025-10-06T14:12:28.527Z" }, + { url = "https://files.pythonhosted.org/packages/bd/e8/37a1e7b99721c0564b1fc7b0a4d1f595ef6fb8060d82ca61775b644185f7/yarl-1.22.0-cp39-cp39-manylinux2014_armv7l.manylinux_2_17_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:e81fda2fb4a07eda1a2252b216aa0df23ebcd4d584894e9612e80999a78fd95b", size = 318902, upload-time = "2025-10-06T14:12:30.528Z" }, + { url = "https://files.pythonhosted.org/packages/1c/ef/34724449d7ef2db4f22df644f2dac0b8a275d20f585e526937b3ae47b02d/yarl-1.22.0-cp39-cp39-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:99b6fc1d55782461b78221e95fc357b47ad98b041e8e20f47c1411d0aacddc60", size = 363302, upload-time = "2025-10-06T14:12:32.295Z" }, + { url = "https://files.pythonhosted.org/packages/8a/04/88a39a5dad39889f192cce8d66cc4c58dbeca983e83f9b6bf23822a7ed91/yarl-1.22.0-cp39-cp39-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:088e4e08f033db4be2ccd1f34cf29fe994772fb54cfe004bbf54db320af56890", size = 370816, upload-time = "2025-10-06T14:12:34.01Z" }, + { url = "https://files.pythonhosted.org/packages/6b/1f/5e895e547129413f56c76be2c3ce4b96c797d2d0ff3e16a817d9269b12e6/yarl-1.22.0-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e4e1f6f0b4da23e61188676e3ed027ef0baa833a2e633c29ff8530800edccba", size = 346465, upload-time = "2025-10-06T14:12:35.977Z" }, + { url = "https://files.pythonhosted.org/packages/11/13/a750e9fd6f9cc9ed3a52a70fe58ffe505322f0efe0d48e1fd9ffe53281f5/yarl-1.22.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:84fc3ec96fce86ce5aa305eb4aa9358279d1aa644b71fab7b8ed33fe3ba1a7ca", size = 341506, upload-time = "2025-10-06T14:12:37.788Z" }, + { url = "https://files.pythonhosted.org/packages/3c/67/bb6024de76e7186611ebe626aec5b71a2d2ecf9453e795f2dbd80614784c/yarl-1.22.0-cp39-cp39-musllinux_1_2_armv7l.whl", hash = "sha256:5dbeefd6ca588b33576a01b0ad58aa934bc1b41ef89dee505bf2932b22ddffba", size = 335030, upload-time = "2025-10-06T14:12:39.775Z" }, + { url = "https://files.pythonhosted.org/packages/a2/be/50b38447fd94a7992996a62b8b463d0579323fcfc08c61bdba949eef8a5d/yarl-1.22.0-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:14291620375b1060613f4aab9ebf21850058b6b1b438f386cc814813d901c60b", size = 358560, upload-time = "2025-10-06T14:12:41.547Z" }, + { url = "https://files.pythonhosted.org/packages/e2/89/c020b6f547578c4e3dbb6335bf918f26e2f34ad0d1e515d72fd33ac0c635/yarl-1.22.0-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:a4fcfc8eb2c34148c118dfa02e6427ca278bfd0f3df7c5f99e33d2c0e81eae3e", size = 357290, upload-time = "2025-10-06T14:12:43.861Z" }, + { url = "https://files.pythonhosted.org/packages/8c/52/c49a619ee35a402fa3a7019a4fa8d26878fec0d1243f6968bbf516789578/yarl-1.22.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:029866bde8d7b0878b9c160e72305bbf0a7342bcd20b9999381704ae03308dc8", size = 350700, upload-time = "2025-10-06T14:12:46.868Z" }, + { url = "https://files.pythonhosted.org/packages/ab/c9/f5042d87777bf6968435f04a2bbb15466b2f142e6e47fa4f34d1a3f32f0c/yarl-1.22.0-cp39-cp39-win32.whl", hash = "sha256:4dcc74149ccc8bba31ce1944acee24813e93cfdee2acda3c172df844948ddf7b", size = 82323, upload-time = "2025-10-06T14:12:48.633Z" }, + { url = "https://files.pythonhosted.org/packages/fd/58/d00f7cad9eba20c4eefac2682f34661d1d1b3a942fc0092eb60e78cfb733/yarl-1.22.0-cp39-cp39-win_amd64.whl", hash = "sha256:10619d9fdee46d20edc49d3479e2f8269d0779f1b031e6f7c2aa1c76be04b7ed", size = 87145, upload-time = "2025-10-06T14:12:50.241Z" }, + { url = "https://files.pythonhosted.org/packages/c2/a3/70904f365080780d38b919edd42d224b8c4ce224a86950d2eaa2a24366ad/yarl-1.22.0-cp39-cp39-win_arm64.whl", hash = "sha256:dd7afd3f8b0bfb4e0d9fc3c31bfe8a4ec7debe124cfd90619305def3c8ca8cd2", size = 82173, upload-time = "2025-10-06T14:12:51.869Z" }, + { url = "https://files.pythonhosted.org/packages/73/ae/b48f95715333080afb75a4504487cbe142cae1268afc482d06692d605ae6/yarl-1.22.0-py3-none-any.whl", hash = "sha256:1380560bdba02b6b6c90de54133c81c9f2a453dee9912fe58c1dcced1edb7cff", size = 46814, upload-time = "2025-10-06T14:12:53.872Z" }, +] + +[[package]] +name = "zipp" +version = "3.23.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e3/02/0f2892c661036d50ede074e376733dca2ae7c6eb617489437771209d4180/zipp-3.23.0.tar.gz", hash = "sha256:a07157588a12518c9d4034df3fbbee09c814741a33ff63c05fa29d26a2404166", size = 25547, upload-time = "2025-06-08T17:06:39.4Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2e/54/647ade08bf0db230bfea292f893923872fd20be6ac6f53b2b936ba839d75/zipp-3.23.0-py3-none-any.whl", hash = "sha256:071652d6115ed432f5ce1d34c336c0adfd6a884660d1e9712a256d3d3bd4b14e", size = 10276, upload-time = "2025-06-08T17:06:38.034Z" }, +]