Skip to content

Releases: StanfordFSL/moonlight-python

v0.3.0

Choose a tag to compare

@MarcSchlichting MarcSchlichting released this 09 Aug 00:58
3975060

v0.3.0

New Features

  • Audio capture — Sunshine's Opus stream is decoded to 48 kHz float32 PCM and
    exposed through client.audio() (iterator) and client.latest_audio()
    (latest-chunk buffer), mirroring the video frame APIs. On by default via
    record_audio=True on start_stream() / stream().
  • Audio in recordings — record() and start_recording() now include audio
    automatically: a synced AAC track inside video files, or a companion audio.wav
    next to image-directory captures. Pass with_audio=False to skip it for a
    given recording.
  • New public types — AudioChunk (PCM + timing metadata), AudioDecoder
    (PyAV/Opus, stereo and surround), and WavRecorder (16-bit PCM output).
  • VideoRecorder(audio=True) — Adds an AAC track to the same container, fed by
    a thread-safe write_audio(), for direct use of the recorder classes.
  • version — The package now exposes its installed version.

Bug Fixes

  • Fixed stretched video timelines — Video PTS moved from a 1/fps time base to
    90 kHz, and from the local clock to the host's capture timestamps. Frames
    arriving in bursts are no longer pushed forward a full frame period each,
    which previously stretched recordings past wall clock.
  • Audio loss no longer shifts the timeline — Audio is positioned at
    frame_index × samples_per_frame rather than appended, so packets lost on the
    network, dropped by a full queue, or corrupt leave a gap of silence instead of
    pulling all later audio earlier.
  • No truncated audio tail — On stop, in-flight audio gets a bounded drain
    window and any remaining deficit is padded with silence, so both tracks end
    together.

Breaking Changes

  • VideoRecorder.write(frame, pts=) is now write(frame,
    pts_us=). This only affects direct use of the recorder class;
    the MoonlightClient API is unchanged.
  • Audio capture is enabled by default, so streams install audio callbacks and
    run an Opus decode thread, and recordings gain an audio track. Pass
    record_audio=False to start_stream() / stream() for the previous behavior.

Improvements

  • Audio loss diagnostics are logged (packets lost or dropped, silence
    inserted, gaps bridged) — enable logging at INFO to see them.
  • CI: full-history checkouts so setuptools-scm resolves version tags, macOS
    runner pinned to macos-15, and the wheel matrix no longer fails fast.

v0.2.0

Choose a tag to compare

@MarcSchlichting MarcSchlichting released this 06 Mar 05:42

v0.2.0

New Features

  • Shared stream architecture — New start_stream() / stop_stream() lifecycle replaces per-call streaming. Once a stream is started, frames(), latest_frame(), record(), and capture() all tap into the shared connection instead of creating their own.
  • stream() context manager — Convenience wrapper around start_stream() / stop_stream() for scoped usage.
  • capture() method — Single-screenshot helper that grabs the latest frame and saves it as an image.
  • start_recording() / stop_recording() — Background recording to video or image directory, running in a separate thread until explicitly stopped.
  • frames() iterator — Dedicated method for iterating decoded frames from the shared stream.
  • Auto-pairing in connect() — connect() now automatically pairs with the server if needed, removing the need to call pair() separately.
  • Windows and macOS wheel builds — CI workflows for building and publishing wheels on all three platforms.

Bug Fixes

  • Fixed auto-pair detection — Pairing status is now checked via an authenticated HTTPS request (mutual TLS) instead of the HTTP /serverinfo endpoint, which couldn't identify the client. Previously, connect() would trigger pairing on every call even for already-paired clients.
  • Fixed IDR frame requests — Resolved issues with IDR frame delivery when multiple clients connect, using a mouse-movement workaround.
  • Fixed video aspect ratio — Corrected aspect ratio distortion in recorded video output.
  • Fixed custom port handling — Non-default server ports now work correctly throughout the connection and streaming flow.
  • Fixed black-start recording — Recordings now capture the latest available frame first to avoid starting with black frames.
  • Fixed VFR recording — Video recording uses wall-clock PTS timestamps, producing correct playback timing for variable-framerate streams instead of assuming constant framerate.

Improvements

  • StreamNotActiveError exception — New exception raised when calling stream-dependent methods without an active stream, with a clear message to call start_stream() first.

Initial Release

Choose a tag to compare

@MarcSchlichting MarcSchlichting released this 04 Mar 03:55

v0.1.0 — Initial Release

First public release of moonlight-python, a Python client for Moonlight/Sunshine game streaming that delivers decoded video frames as numpy arrays.

Features

  • Server discovery — automatic mDNS discovery or direct IP connection
  • Cryptographic pairing — PIN-based pairing compatible with Sunshine and NVIDIA GameStream
  • Frame streaming — receive decoded H.264/HEVC/AV1 frames as numpy arrays via a simple generator API
  • Latest frame buffer — thread-safe drop-oldest buffer for CV pipelines that process slower than the stream rate
  • Recording — save to video files (MP4) or image sequences (PNG/JPEG)
  • Codec support — H.264, HEVC, and AV1

Installation

pip install moonlight-python

Pre-built manylinux wheels are available for Python 3.10–3.13 on x86_64 Linux. No compiler or system dependencies required.

Built on

  • moonlight-common-c — core streaming protocol via CFFI
  • PyAV (FFmpeg) — hardware-accelerated video decoding