Skip to content

Add WebRTC Camera Streaming Support and Related Documentation - #24

Draft
dcode wants to merge 1 commit into
mainfrom
feat/webrtc
Draft

Add WebRTC Camera Streaming Support and Related Documentation#24
dcode wants to merge 1 commit into
mainfrom
feat/webrtc

Conversation

@dcode

@dcode dcode commented Mar 10, 2026

Copy link
Copy Markdown
Owner

This PR introduces comprehensive support for low-latency WebRTC camera streaming directly from the CLI and SDK, allowing users and developers to natively view and integrate live camera feeds from their Prusa 3D printers.

We've bridged the gap between the Prusa Connect WebRTC signaling server and local visualization, drastically simplifying the workflow necessary to view real-time video outputs.

Key Features & Changes

  1. CLI camera webrtc Command

    • Added a new prusactl camera webrtc <camera-id> command that dynamically handles pulling camera tokens
      and SDK JWT credentials.
    • Using a newly implemented webrtc_template.html template, the command seamlessly stitches together the necessary
      configuration payloads alongside the local camera_v2.proto definitions to construct a client-ready WebRTC viewer.
    • Automatically saves the generated player and pops open the default web browser for true zero-friction streaming.
  2. Google-Style Docstrings Adoption

    • Refactored all methods in
    • src/prusa/connect/client/cli/commands/camera.py
    • to adhere to the comprehensive Google-style format (Args:, Returns:, Raises:).
    • This drastically improves the auto-generated documentation built by mkdocstrings and cyclopts
      to ensure our API contracts are fully illuminated for community developers.
  3. MkDocs Integration and Guides

    • CLI Reference: Improved the docs/cli/quickstart.md to cleanly showcase prusactl camera webrtc right in the flow
      of the broader Work with Teams and Cameras segment.
    • SDK Developer Examples: Created a dedicated WebRTC Camera Streaming block natively right
      inside docs/examples.md to equip developers with the snippet they need to bypass the CLI and extract exactly
      the camera_token, jwt_token, and proto configurations via the PrusaConnectClient.

Testing & Verification

  • Validated WebRTC stream integrity and dynamic HTML generation natively on the host machine.
  • Resolved trailing whitespace lint errors (Ruff) directly in camera.py
  • Ran mkdocs build --strict locally to guarantee no broken links and high-quality docstring parsing.

@gemini-code-assist

Copy link
Copy Markdown

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly expands the Prusa Connect client's capabilities by integrating low-latency WebRTC camera streaming. It provides both a command-line interface for users to easily view live camera feeds from their 3D printers and SDK examples for developers to integrate this functionality into their applications. The changes also improve the project's documentation quality and build process by adopting new documentation tools and standards.

Highlights

  • WebRTC Camera Streaming: Introduced a new prusactl camera webrtc <camera-id> CLI command for zero-friction WebRTC camera streaming, dynamically generating an HTML player for live feeds.
  • Improved Docstrings: Refactored all methods in src/prusa/connect/client/cli/commands/camera.py to adhere to Google-style docstrings, enhancing auto-generated documentation.
  • Enhanced Documentation: Updated the CLI quickstart guide to include the new WebRTC command and added a dedicated SDK developer example for WebRTC camera streaming in docs/examples.md.
Changelog
  • docs/api/openapi-reference.md
    • Added a new documentation page to display OpenAPI specifications.
  • docs/cli/quickstart.md
    • Updated the quickstart guide to include the new prusactl camera webrtc command.
  • docs/css/mkdocsoad.css
    • Added a new CSS file to support OpenAPI documentation styling.
  • docs/development_notes/rest_api_reference.md
    • Updated the introductory note to direct users to the new OpenAPI documentation for API details.
  • docs/examples.md
    • Added a new section demonstrating how to use the SDK for WebRTC camera streaming.
  • mkdocs.yml
    • Configured pymdownx.details markdown extension.
    • Enabled the neoteroi.mkdocsoad plugin.
    • Included mkdocsoad.css in extra CSS files.
    • Added 'OpenAPI Reference' to the navigation menu.
  • proto/prusa/connect/client/camera_v2.proto
    • Extended the camera protobuf definition with new messages for WebRTC signaling, including WebRTCServer, WebRTCConfig, WebRTCData, and WebRTCSignaling.
  • pyproject.toml
    • Pinned grpcio and grpcio-tools to version 1.78.0 to address a known issue.
    • Added essentials-openapi and neoteroi-mkdocs to documentation dependencies.
    • Configured hatch to force-include proto files in the wheel build to ensure they are packaged correctly.
  • src/prusa/connect/client/auth.py
    • Refined comments for clarity regarding token refresh responses.
    • Added commented-out debug logging for various stages of the authentication flow (initial login request, credentials submission, TOTP response, token exchange).
  • src/prusa/connect/client/camera.py
    • Consolidated WebRTC event handling from multiple specific callbacks (_on_webrtc_offer, _on_webrtc_answer, _on_webrtc_ice_candidate) into a single, unified _on_webrtc callback.
    • Introduced a new webrtc_send method for sending WebRTC signaling messages to the server, abstracting the protobuf serialization and socket emission.
  • src/prusa/connect/client/cli/commands/camera.py
    • Implemented the camera webrtc command, which handles token resolution, JWT extraction, loading a WebRTC HTML template, embedding protobuf definitions, and opening the generated player in a web browser.
    • Applied Google-style docstrings to all existing camera CLI commands (camera_list, cameras_alias, camera_snapshot, camera_trigger, camera_move, camera_adjust, set_current_camera, camera_show) for improved documentation generation.
  • src/prusa/connect/client/cli/templates/webrtc_template.html
    • Added a new HTML template that serves as a client-side WebRTC player, dynamically populated with camera and JWT tokens, and protobuf definitions for real-time streaming.
Activity
  • The pull request was created by dcode, introducing significant new features and documentation.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces significant new functionality for WebRTC camera streaming, both in the CLI and as an SDK example. However, the camera webrtc CLI command has critical security vulnerabilities, specifically Cross-Site Scripting (XSS) and Path Traversal, due to injecting unvalidated user input into the HTML template and using it for temporary file paths. These could lead to arbitrary script execution or unintended file writes. Beyond these security concerns, the implementation could be improved for robustness and maintainability by addressing the use of private SDK members, removing debug code, making development-mode file path resolution less fragile, fixing a potential race condition in the WebRTC JavaScript template, and refactoring a long function.

# Escape backticks for javascript template literal
proto_content = proto_content.replace("`", "\\`")

html_content = html_content.replace("{{ CAMERA_TOKEN }}", token)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-high high

The camera webrtc command is vulnerable to Cross-Site Scripting (XSS). The user-provided camera_id (which becomes the token variable) is injected directly into a JavaScript string literal in the generated HTML file using simple string replacement. An attacker could provide a malicious camera_id such as "; alert('XSS'); // which would result in the following JavaScript being executed when the user opens the stream: const CAMERA_TOKEN = ""; alert('XSS'); //";. Since the user's JWT token is also embedded in this same file, an attacker could use this XSS to exfiltrate the session token.

Remediation: Sanitize or escape the token before injecting it into the HTML template. A safe way to escape a string for a JavaScript literal is to use json.dumps().

Suggested change
html_content = html_content.replace("{{ CAMERA_TOKEN }}", token)
import json
html_content = html_content.replace("{{ CAMERA_TOKEN }}", json.dumps(token)[1:-1])

Comment on lines +353 to +355
# Fallback for local development tree
repo_root = pathlib.Path(__file__).resolve().parent.parent.parent.parent.parent.parent.parent
proto_path = repo_root / "proto" / "prusa" / "connect" / "client" / "camera_v2.proto"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The fallback logic to find the .proto file by chaining .parent seven times is very fragile and will break if the directory structure changes. A more robust method should be used to locate the project root in a development environment.

Consider searching upwards from the current file for a project marker like pyproject.toml.

Here's a more robust implementation suggestion:

# Fallback for local development tree
current_path = pathlib.Path(__file__).resolve()
# Search for a file that indicates the project root, e.g., 'pyproject.toml'
repo_root = current_path
while not (repo_root / 'pyproject.toml').exists() and repo_root.parent != repo_root:
    repo_root = repo_root.parent

if not (repo_root / 'pyproject.toml').exists():
    common.output_message("Could not determine project root for development fallback.", error=True)
    return

proto_path = repo_root / "proto" / "prusa" / "connect" / "client" / "camera_v2.proto"

Comment on lines +161 to +162
// Wait 500ms for auth to process, then send triggers and request CONFIG
setTimeout(() => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Using a fixed setTimeout to wait for authentication introduces a race condition. If authentication takes longer than 500ms due to network latency or server load, the subsequent trigger and webrtc events will be sent prematurely and may fail. A more reliable approach is to wait for an explicit confirmation event from the server (e.g., an authenticated or ready event) before proceeding with the next steps in the connection sequence.

# Save to temp file and open
import tempfile

temp_file = pathlib.Path(tempfile.gettempdir()) / f"prusa_webrtc_{token}.html"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The camera webrtc command is vulnerable to Path Traversal. The user-provided camera_id (as token) is used to construct the filename for a temporary HTML file. An attacker could provide a camera_id containing path traversal sequences (e.g., ../../) to attempt to write the HTML file to an arbitrary location on the filesystem.

Remediation: Use a securely generated random filename for the temporary file instead of incorporating user-controlled input.

Suggested change
temp_file = pathlib.Path(tempfile.gettempdir()) / f"prusa_webrtc_{token}.html"
import uuid
temp_file = pathlib.Path(tempfile.gettempdir()) / f"prusa_webrtc_{uuid.uuid4().hex}.html"

Comment thread docs/examples.md
Comment on lines +68 to +69
if hasattr(client, "_credentials") and hasattr(client._credentials, "tokens"):
jwt_token = client._credentials.tokens.access_token.raw_token

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing private members like _credentials is not a safe practice as it relies on internal implementation details that can change without notice, potentially breaking your code in future SDK versions. It would be more robust to add a public method to the PrusaConnectClient for retrieving the JWT, for example client.get_raw_jwt().

Comment on lines +396 to +400
# logger.debug("Initial login request", method="GET", params=params,
# url=consts.AUTH_URL)
resp = session.get(consts.AUTH_URL, params=params)
# logger.debug("Initial login flow reponse", status_code=resp.status_code,
# text=resp.text, headers=resp.headers)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This file contains several commented-out logger.debug calls. This kind of code adds clutter and should be removed before merging to keep the codebase clean. If these logs are needed for debugging, they should be controlled by a logging level configuration rather than being commented out.

Comment on lines +328 to +329
if hasattr(client, "_credentials") and hasattr(client._credentials, "tokens"):
jwt_token = client._credentials.tokens.access_token.raw_token

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Accessing private members like _credentials and tokens is brittle and can break with future SDK updates. The comment "We reach into private credentials safely" is misleading, as this pattern is inherently unsafe. A much better approach would be to expose a public method on PrusaConnectClient for this purpose, such as get_raw_jwt().

- Add WebRTC signaling message definitions to `camera_v2.proto`.
- Refactor `PrusaCameraClient` in `camera.py` to use a single `webrtc` event listener and add a `webrtc_send` method for handling protobuf-encoded signaling messages.
- Add new `camera webrtc` CLI command to generate and open a local WebRTC player for the camera stream.
- Add `webrtc_template.html` template that implements the WebRTC signaling flow (STUN/TURN config, SDP offer/answer, ICE candidates) using socket.io and protobuf.js to stream the camera video directly in the browser.
- Bundle camera_v2.proto into package to inject into web template.
- Update documentation to cover both SDK and CLI usage of webrtc feature.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant