xlambda-media
Image, video, and 360°-tour upload and transforms, plus live streaming (RTMP/SRT/WHIP ingest, WHEP/HLS playback).
pip install xlambda-media
Python port of @xlambda-tech/media.
Uploading
The platform's upload is a three-call dance — presign, PUT the bytes to storage, confirm. This collapses it into one call:
from xlambda.media import MediaClient
client = MediaClient(api_key=os.environ["XLAMBDA_API_KEY"])
result = client.media.upload(
file=Path("photo.jpg"), # bytes, a path, or any binary file object
filename="photo.jpg",
content_type="image/jpeg",
project_id="proj_123",
)
print(result["mediaId"])
A path is streamed rather than read into memory, which matters for video. The size is computed for you.
If the transfer to storage fails, you get an UploadTransferError carrying
the media_id:
from xlambda.media import UploadTransferError
try:
client.media.upload(file=data, filename="a.mp4", content_type="video/mp4")
except UploadTransferError as err:
log.warning("orphaned pending media %s: %s", err.media_id, err)
That id matters — the presign already created a row server-side, and there's no automatic cleanup, so losing the id loses the record.
Batches presign in one round trip and report per-file outcomes, index-aligned with the input, so one bad file doesn't sink the rest:
results = client.media.upload_batch(files=[
{"file": a, "filename": "a.jpg", "contentType": "image/jpeg"},
{"file": b, "filename": "b.jpg", "contentType": "image/jpeg"},
])
# [{"success": True, "mediaId": ...}, {"success": False, "error": ...}]
The sync version transfers sequentially; AsyncMediaClient.media.upload_batch
runs them concurrently via asyncio.gather, matching the npm SDK.
upload_stream() is the one-step alternative — bytes go through the platform
as multipart instead of direct-to-storage, with no confirm step. Prefer
upload() for anything client-originated; use this for server-to-server
piping where you already hold the bytes.
Image transforms
A pure URL builder — no network call, no auth header, because there's nowhere
to put one on an <img> tag:
from xlambda.media import transform_url
client.image.transform_url("med_123", width=800, format="webp", quality=90)
# https://api.xlambda.tech/v1/image/med_123?width=800&format=webp&quality=90
crop="fill" requires both width and height, and raises ValueError if
you omit one rather than sending a request that would fail server-side.
Live streaming
stream = client.livestreams.create(project_id="proj_123", title="Launch", record_enabled=True)
stream["rtmpServerUrl"], stream["rtmpStreamKey"] # paste into OBS
stream["hlsPlaybackUrl"] # for players
stream["webRtcIngestUrl"], stream["webRtcPlaybackUrl"] # WHIP / WHEP
The ingest secrets come back only from create() and regenerate_key() —
the plaintext stream key is never retrievable again. regenerate_key() kicks
the current publisher immediately and needs the admin scope.
Simulcast and clips hang off a stream id:
client.livestreams.simulcast_targets(stream["id"]).create(
label="YouTube", destination_url="rtmp://a.rtmp.youtube.com/live2/KEY"
)
client.livestreams.clips(stream["id"]).create(duration_seconds=30)
WHIP/WHEP are URLs for a browser or player to use directly; this SDK doesn't wrap the SDP exchange.
Webhooks
from xlambda.media import create_media_events_handler
from xlambda.core.webhooks.adapters.flask import create_flask_blueprint
handler = create_media_events_handler(
secret=os.environ["XLAMBDA_WEBHOOK_SECRET"],
on_ready=lambda data: Media.objects.filter(pk=data["mediaId"]).update(status="ready"),
on_failed=lambda data: log.error("processing failed: %s", data["error"]),
on_livestream_recorded=lambda data: attach_recording(data["liveStreamId"], data["mediaId"]),
)
app.register_blueprint(create_flask_blueprint(handler), url_prefix="/webhooks/media")
Every callback is optional, and anything that isn't a media.*/livestream.*
event is acknowledged with a 200 and ignored — so pointing a shared webhook
URL here is safe. create_async_media_events_handler is the FastAPI-side
twin. See xlambda-core for the adapters and the
raw-body rule.
Async
Everything above has an awaitable twin:
from xlambda.media import AsyncMediaClient
async with AsyncMediaClient(api_key=...) as client:
await client.media.upload(file=data, filename="a.jpg", content_type="image/jpeg")
client.image.transform_url() stays synchronous on both — it builds a string.
Conventions
Arguments are snake_case; responses are the API's own camelCase, returned as
plain dicts typed by TypedDict. See xlambda-core.
Metadata
Release files for xlambda-media 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| xlambda_media-0.2.0.tar.gz | 15.0 kB | With provenance | Details for xlambda_media-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| xlambda_media-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for xlambda_media-0.2.0-py3-none-any.whl |
Total release size: 32.1 kB
Release files / xlambda_media-0.2.0.tar.gz
| Download URL | xlambda_media-0.2.0.tar.gz |
|---|---|
| Size | 15.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8ff1fa31306d8312ee39d28103d78623b1cfcf9a23ca9bc8ec721f8b0099618b
|
|
BLAKE2b-256 checksum How to use checksums |
4e67658d4a3e20b138e93a1ee35e8589ffe9bbc8bdf55b7c2977f8d785c995ec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logRelease files / xlambda_media-0.2.0-py3-none-any.whl
| Download URL | xlambda_media-0.2.0-py3-none-any.whl |
|---|---|
| Size | 17.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
313ba4d3b4a333ff72ce1da9f8e761223806760557aa212be7401685beea3ab1
|
|
BLAKE2b-256 checksum How to use checksums |
2df9f67e8bc964c33c2b6c4783f37d34a31e1ae4984b2ded19d3629191ca1dfe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logSecurity for xlambda-media 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for xlambda_media-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.