A FastAPI server with two pages and one WebSocket. /mirror captures a window, screen or tab through the OS-native getDisplayMedia picker; /watch shows every live mirror as a wall and takes one fullscreen on click. Any number of sharers and watchers, at the same time. Media never touches the server: each watcher gets its own RTCPeerConnection to the sharer, and the Python process only relays JSON. The roster lives in registry.py, which is transport-free by design -- every operation returns the envelopes to deliver, so admission, fan-out and teardown policy are all unit-testable without a socket. Registry rules worth stating: a signal frame is relayed only along an existing subscription in either direction; viewer-to-peer traffic is refused; and teardown is broadcast to every viewer rather than only the subscribers, because a tile is drawn on stream_added and a viewer that has not yet subscribed still has a stale tile to clear. Frontend is hand-written ES modules with no build step and no external asset. Chromium scales a monitor capture to the capturing window and requires a secure context, so /mirror detects an insecure origin and prints the three ways out rather than failing on a button click. LLM-Generated: true
51 lines
1.5 KiB
Docker
51 lines
1.5 KiB
Docker
# mirror — browser-only screen mirroring.
|
|
#
|
|
# podman build -t mirror .
|
|
# podman run --rm -p 8055:8055 mirror
|
|
#
|
|
# The image serves the app; it does not capture anything. Capture happens in the
|
|
# viewer's and sharer's own browsers, so there is no display, D-Bus, or PipeWire
|
|
# socket to pass in.
|
|
|
|
FROM docker.io/library/python:3.13-slim AS builder
|
|
|
|
ENV PIP_DISABLE_PIP_VERSION_CHECK=1 UV_LINK_MODE=copy
|
|
RUN pip install --no-cache-dir uv
|
|
|
|
WORKDIR /app
|
|
COPY pyproject.toml README.md ./
|
|
COPY src ./src
|
|
RUN uv venv /opt/venv \
|
|
&& uv pip install --python /opt/venv/bin/python --no-cache .
|
|
|
|
|
|
FROM docker.io/library/python:3.13-slim
|
|
|
|
LABEL org.opencontainers.image.title="mirror" \
|
|
org.opencontainers.image.description="Browser-only screen mirroring for the local network" \
|
|
org.opencontainers.image.source="https://example.invalid/mirror"
|
|
|
|
# curl is for the HEALTHCHECK only; nothing else in the runtime image reaches out.
|
|
RUN apt-get update \
|
|
&& apt-get install -y --no-install-recommends curl \
|
|
&& rm -rf /var/lib/apt/lists/*
|
|
|
|
COPY --from=builder /opt/venv /opt/venv
|
|
ENV PATH="/opt/venv/bin:${PATH}" \
|
|
PYTHONUNBUFFERED=1 \
|
|
PYTHONDONTWRITEBYTECODE=1 \
|
|
MIRROR_HOST=0.0.0.0 \
|
|
MIRROR_PORT=8055
|
|
|
|
# Never run the web process as root.
|
|
RUN useradd --create-home --shell /usr/sbin/nologin mirror
|
|
USER mirror
|
|
WORKDIR /home/mirror
|
|
|
|
EXPOSE 8055
|
|
HEALTHCHECK --interval=20s --timeout=3s --start-period=5s --retries=3 \
|
|
CMD curl -fsS http://127.0.0.1:8055/healthz || exit 1
|
|
|
|
ENTRYPOINT ["mirror"]
|
|
CMD ["serve"]
|