Open edX XBlock that embeds Bunny.net Stream videos with Token Authentication, TUS upload, and encoding-status webhooks.
Project description
xblock-bunny
Open edX XBlock that embeds Bunny.net Stream videos with Token Authentication, direct-to-Bunny TUS upload from Studio, and encoding-status webhooks.
Status: 0.1.0 — early. Issues / PRs welcome at https://github.com/amirtds/bunny-xblock.
Compatibility
| Component | Supported versions | Notes |
|---|---|---|
| Open edX | Sumac, Teak, and Ulmo (open-release/{sumac,teak,ulmo}.master) |
Earlier releases (Quince, Redwood, Palm) haven't been tested — likely works, no guarantee. The XBlock self-wires through the stable lms.djangoapp / cms.djangoapp plugin entry points, so forward compatibility with future named releases is expected. |
| Tutor | 19.x (Sumac), 20.x (Teak), 21.x (Ulmo) | The OPENEDX_EXTRA_PIP_REQUIREMENTS install path is identical across all three. |
| Python | 3.11 (matches Tutor Sumac / Teak) | The package itself declares python>=3.8 so it's also installable in older runtimes. |
| Django | 4.2 LTS | Same Django Open edX ships with. |
| Bunny.net | Stream — any plan | DRM (MediaCage Premium) is not required. Token Authentication is in the free tier. |
Authoring is browser-based: any browser that supports fetch + Promise + URLSearchParams (effectively Chrome / Firefox / Safari / Edge from the last 5 years). The LMS player uses a vanilla <iframe> so it inherits Bunny's playback compatibility — including iOS Safari, where the embedded player falls back to AirPlay-friendly HLS.
Why
The default Open edX VideoBlock doesn't know how to play iframe.mediadelivery.net URLs and the dominant community alternative (appsembler/xblock-video) ships only Brightcove / HTML5 / Vimeo / Wistia / YouTube backends — no Bunny. This XBlock fills the gap: it lets course authors upload directly to a configured Bunny library from inside a Studio unit and renders Token-Authenticated signed iframes in the LMS so videos can't be hot-linked.
Install
The XBlock self-wires via Open edX's Django plugin entry points — no edx-platform edits, no INSTALLED_APPS patching, no lms.env.json / cms.env.json changes.
On Tutor (Sumac / Teak)
tutor config save --append OPENEDX_EXTRA_PIP_REQUIREMENTS=xblock-bunny
tutor local launch
That's it. Open the LMS as a global admin, paste credentials at:
https://<your-lms>/admin/xblock_bunny/bunnyconfiguration/
Then in Studio: course → Advanced Settings → "Advanced Module List" → add bunny_video. The "Bunny Video" component now appears in the unit "Add New Component" menu.
Configuration fields
| Field | Where to find it in Bunny |
|---|---|
| Library ID | Stream → Library → API |
| API Key | Stream → Library → API |
| Security Key | Stream → Library → Security → "Token Authentication Key" (enable Token Authentication first) |
| CDN Hostname | Stream → Library → API → "Pull Zone Hostname" — looks like vz-xxxxxxxx-xxx.b-cdn.net |
After saving, the page displays a Webhook URL. Paste it into Bunny → Stream → Library → Webhooks so the XBlock receives encoding-status updates.
Authoring (inline UX)
In a Studio unit, "Bunny Video" gives you a single drop zone:
- Drag a file or click "Choose video".
- Watch the upload progress bar (TUS direct to Bunny — bytes never touch your Open edX server).
- The block shows "Processing on Bunny…" while encoding.
- The block flips to the signed-iframe player when Bunny reports
ready. The webhook drives this — no page reload required.
You can replace or delete the video from the same surface.
Student playback
The LMS renders a click-to-load poster (using Bunny's auto thumbnail). On click, the iframe is mounted with a freshly signed URL:
https://iframe.mediadelivery.net/embed/<library>/<guid>?token=<sha256(securityKey+guid+expires)>&expires=<unix+6h>
Hot-linking the URL from another origin returns 401 (when Token Authentication is enabled on the Bunny library).
Known limitations
- Mobile apps: the official Open edX iOS / Android apps don't render custom video XBlocks natively — they degrade to a webview. v2 candidate.
- CSP: if your Open edX instance enforces
Content-Security-Policy: frame-src, the package's settings hook addsiframe.mediadelivery.nettoCSP_FRAME_SRCwhen that setting exists. On stricter setups, add it manually. - Cubite-style analytics:
courseware.video.playedevents are hardcoded toVideoBlockand don't fire for this XBlock. v2 candidate.
Architecture
- One Bunny library per Open edX instance — credentials live in a singleton
BunnyConfigurationrow managed via Django admin. API key and Security key are Fernet-encrypted (key derived fromSECRET_KEYvia HKDF). - Upload uses TUS direct to
https://video.bunnycdn.com/tusupload; the server only mints a per-upload signature (sha256(libraryId + apiKey + expires + guid), 1 h TTL). - Embed URLs are signed at render time per the Bunny scheme (
sha256(securityKey + guid + expires), 6 h TTL). - Webhook auth is URL-token-only (Bunny Stream doesn't sign payloads), with library-mismatch and lifecycle-regression guards to mitigate URL leaks.
- Django app auto-discovered via
lms.djangoapp/cms.djangoappentry points (Open edXedx_django_utils.plugins).
License
Apache-2.0. See LICENSE.
This XBlock is maintained by Cubite and works with any Bunny.net account — no Cubite tenancy required.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file xblock_bunny-0.1.0.tar.gz.
File metadata
- Download URL: xblock_bunny-0.1.0.tar.gz
- Upload date:
- Size: 79.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b069d9b654daf52602d99adfd5ccb6ed077bb958077830d1d55fa1ad55e91278
|
|
| MD5 |
8e973c5b0db31e78b9176b2f43ee5983
|
|
| BLAKE2b-256 |
dcd0f78e47c1dc5b5f031428bda9c9256d641795d9f906947b90ba08327e8ab8
|
Provenance
The following attestation bundles were made for xblock_bunny-0.1.0.tar.gz:
Publisher:
release.yml on amirtds/bunny-xblock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xblock_bunny-0.1.0.tar.gz -
Subject digest:
b069d9b654daf52602d99adfd5ccb6ed077bb958077830d1d55fa1ad55e91278 - Sigstore transparency entry: 1542946619
- Sigstore integration time:
-
Permalink:
amirtds/bunny-xblock@8824469b557c9845398e00f2a6712df08cfc8fcd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/amirtds
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8824469b557c9845398e00f2a6712df08cfc8fcd -
Trigger Event:
push
-
Statement type:
File details
Details for the file xblock_bunny-0.1.0-py3-none-any.whl.
File metadata
- Download URL: xblock_bunny-0.1.0-py3-none-any.whl
- Upload date:
- Size: 83.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47b3b601470db171b512e5476802ff099fb5599f63abc3ac4cc0183631198a4d
|
|
| MD5 |
3f7725abfbe4f1ec9f83bdfdcd3b37fa
|
|
| BLAKE2b-256 |
283f472c466b7d94caa4a163bcc820e4c2448a0870c962defa537e3c03db391b
|
Provenance
The following attestation bundles were made for xblock_bunny-0.1.0-py3-none-any.whl:
Publisher:
release.yml on amirtds/bunny-xblock
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
xblock_bunny-0.1.0-py3-none-any.whl -
Subject digest:
47b3b601470db171b512e5476802ff099fb5599f63abc3ac4cc0183631198a4d - Sigstore transparency entry: 1542946718
- Sigstore integration time:
-
Permalink:
amirtds/bunny-xblock@8824469b557c9845398e00f2a6712df08cfc8fcd -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/amirtds
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@8824469b557c9845398e00f2a6712df08cfc8fcd -
Trigger Event:
push
-
Statement type: