cloudflare-stream-video maintained by modularavel
A Laravel package that provides an idiomatic, strongly typed interface to the Cloudflare Stream Video API. Upload, manage, and stream videos with signed URLs, watermark overlays, and webhook-driven processing — all from within your Laravel application.
Features
- Video Management — Upload (TUS), copy from URL, direct upload, list, get, update, and delete videos
- Signed Playback URLs — HMAC-SHA256 signed tokens for restricted video access
- Watermark Management — Create, list, update, delete watermarks; apply/remove watermarks on videos
- Webhook Integration — Receive Cloudflare Stream processing notifications, process them asynchronously via queued jobs, and broadcast completion events
- Blade Player Component — Responsive iframe embed component with signed URL support
- Artisan Commands — CLI commands for video import, sync, signed URL generation, and watermark management
- Config Publishing — All configuration, views, translations, and assets are publishable
Installation
You can install the package via Composer:
composer require modularavel/cloudflare-stream-video
Publishing Resources
You may publish all of the package's resources at once:
php artisan vendor:publish --tag="cloudflare-stream-video"
Or, publish each resource individually:
Configuration
php artisan vendor:publish --tag="cloudflare-stream-video-config"
After publishing, edit config/cloudflare-stream-video.php to configure your Cloudflare account.
Migrations
php artisan vendor:publish --tag="cloudflare-stream-video-migrations"
php artisan migrate
This will create:
cloudflare_stream_videos— Local cache of Cloudflare Stream videoscloudflare_stream_webhooks— Audit log for webhook deliveries
Views
php artisan vendor:publish --tag="cloudflare-stream-video-views"
Translations
php artisan vendor:publish --tag="cloudflare-stream-video-lang"
Public Assets
php artisan vendor:publish --tag="cloudflare-stream-video-assets"
Configuration
After publishing the config file, edit config/cloudflare-stream-video.php to match your Cloudflare account credentials and preferences. Environment variables (with CLOUDFLARE_STREAM_ prefix) take precedence over config file values.
🔑 Required Settings
| 🔧 Key | 🌿 Env Variable | 📝 Description |
|---|---|---|
account_id |
CLOUDFLARE_STREAM_ACCOUNT_ID |
Your Cloudflare account identifier |
api_token |
CLOUDFLARE_STREAM_API_TOKEN |
API token with "Stream Edit" or "Stream Read" permissions |
⚙️ Optional Settings
| 🔧 Key | 🌿 Env Variable | 💡 Default | 📝 Description |
|---|---|---|---|
timeout |
CLOUDFLARE_STREAM_TIMEOUT |
120 |
HTTP request timeout in seconds |
defaults |
— | [] |
Default key-value pairs merged into every API request body |
require_signed_urls |
CLOUDFLARE_STREAM_REQUIRE_SIGNED_URLS |
false |
Whether videos require signed URLs for playback |
signing_key |
CLOUDFLARE_STREAM_SIGNING_KEY |
'' |
HMAC-SHA256 signing key for signed URL generation |
customer_domain |
CLOUDFLARE_STREAM_CUSTOMER_DOMAIN |
'' |
Custom Cloudflare Stream customer domain for signed URLs |
watermark.default_position |
— | 'bottom-right' |
Default watermark position on the video frame |
watermark.default_opacity |
— | 1.0 |
Default watermark opacity (0.0–1.0) |
watermark.default_width |
— | 0 |
Default watermark width in pixels |
watermark.default_height |
— | 0 |
Default watermark height in pixels |
webhook.secret |
CLOUDFLARE_STREAM_WEBHOOK_SECRET |
'' |
Secret used to verify incoming webhook signatures |
webhook.route |
CLOUDFLARE_STREAM_WEBHOOK_ROUTE |
'cloudflare-stream-video/webhook' |
Route path for receiving webhook notifications |
🔐 Environment Example
CLOUDFLARE_STREAM_ACCOUNT_ID=your-account-id
CLOUDFLARE_STREAM_API_TOKEN=your-api-token
CLOUDFLARE_STREAM_SIGNING_KEY=your-64-character-signing-key
CLOUDFLARE_STREAM_WEBHOOK_SECRET=your-webhook-secret
Usage
Basic Video Operations
The CloudflareStreamVideo facade (or inject CloudflareStreamVideoInterface) provides the full API surface.
Upload a Video (TUS)
use Modularavel\CloudflareStreamVideo\Facades\CloudflareStreamVideo;
// Upload a local video file using the TUS resumable protocol.
$video = CloudflareStreamVideo::upload('/path/to/video.mp4', [
'meta' => ['name' => 'My Awesome Video', 'source' => 'laravel'],
'maxDurationSeconds' => 3600,
]);
echo $video->uid; // e.g. "cab807e0c477d01baq20f66c3d1dfc26cf"
echo $video->status; // e.g. "ready"
Copy a Video from a URL
// Fetch and process a video from a publicly accessible URL.
$video = CloudflareStreamVideo::copy('https://example.com/video.mp4', [
'meta' => ['name' => 'Imported Video'],
]);
Create a Direct Upload
// Generate a one-time upload URL for client-side use.
$upload = CloudflareStreamVideo::createDirectUpload([
'maxDurationSeconds' => 300,
'expiry' => now()->addHours(1)->toIso8601String(),
]);
// Pass $upload->uploadUrl to your frontend client.
// The upload URL is valid until expiry or until the video is uploaded.
List Videos
// Retrieve all videos with optional filtering.
$videos = CloudflareStreamVideo::listVideos([
'status' => 'ready',
'limit' => 100,
]);
foreach ($videos as $video) {
echo $video->uid . ' - ' . $video->status;
}
Get a Single Video
$video = CloudflareStreamVideo::getVideo('cab807e0c477d01baq20f66c3d1dfc26cf');
echo $video->uid;
echo $video->status;
echo $video->duration;
echo $video->getHlsUrl(); // e.g. "https://customer.cloudflarestream.com/.../manifest/video.m3u8"
echo $video->getDashUrl(); // e.g. "https://customer.cloudflarestream.com/.../manifest/video.mpd"
echo $video->getEmbedUrl(); // e.g. "https://embed.videodelivery.net/embed/iframe/..."
Update a Video
$video = CloudflareStreamVideo::updateVideo('cab807e0c477d01baq20f66c3d1dfc26cf', [
'meta' => ['name' => 'Updated Title', 'tags' => 'tutorial,php'],
'requireSignedURLs' => true,
]);
Delete a Video
$deleted = CloudflareStreamVideo::deleteVideo('cab807e0c477d01baq20f66c3d1dfc26cf');
Signed URLs
When a signing key is configured, you can generate signed playback URLs and tokens.
use Modularavel\CloudflareStreamVideo\Facades\CloudflareStreamVideo;
// Generate a full signed embed URL (valid for 2 hours by default).
$signedUrl = CloudflareStreamVideo::signedEmbedUrl(
'cab807e0c477d01baq20f66c3d1dfc26cf',
now()->addHours(2)->toDateTimeString(),
);
// Generate only the signed token (for use in custom embed URLs or HLS/DASH manifests).
$token = CloudflareStreamVideo::signedToken(
'cab807e0c477d01baq20f66c3d1dfc26cf',
now()->addHours(2)->toDateTimeString(),
);
Watermarking
Watermarks are overlay images (typically PNG or SVG with transparency) that can be applied to videos during playback. They are managed per-account and associated with videos via their UID.
Create a Watermark
use Modularavel\CloudflareStreamVideo\Facades\CloudflareStreamVideo;
$watermark = CloudflareStreamVideo::createWatermark(
name: 'Brand Logo',
url: 'https://example.com/watermark.png',
position: 'bottom-right',
opacity: 0.8,
width: 200,
height: 50,
);
echo $watermark->uid; // e.g. "wm-brand-123"
List All Watermarks
$watermarks = CloudflareStreamVideo::listWatermarks();
foreach ($watermarks as $wm) {
echo $wm->uid . ' - ' . $wm->name;
}
Get a Single Watermark
$watermark = CloudflareStreamVideo::getWatermark('wm-brand-123');
Update a Watermark
$watermark = CloudflareStreamVideo::updateWatermark('wm-brand-123', [
'opacity' => 0.5,
'position' => 'top-left',
]);
Delete a Watermark
$deleted = CloudflareStreamVideo::deleteWatermark('wm-brand-123');
Apply a Watermark to a Video
$video = CloudflareStreamVideo::applyWatermark(
'cab807e0c477d01baq20f66c3d1dfc26cf',
'wm-brand-123',
);
Remove a Watermark from a Video
$video = CloudflareStreamVideo::removeWatermark('cab807e0c477d01baq20f66c3d1dfc26cf');
Webhook Integration
Cloudflare Stream can send webhook notifications when video processing events occur (e.g. video.ready, video.error). The package provides a webhook endpoint, a background job for async processing, and an event for broadcasting completion.
Setup
- Configure the webhook secret in your
.env:
CLOUDFLARE_STREAM_WEBHOOK_SECRET=your-webhook-secret
- Configure the webhook route (optional, defaults to
cloudflare-stream-video/webhook):
CLOUDFLARE_STREAM_WEBHOOK_ROUTE=cloudflare-stream-video/webhook
- Register the webhook URL in the Cloudflare Stream dashboard under Notifications -> Webhooks. The URL will be:
https://your-app.com/cloudflare-stream-video/webhook
Handling Webhook Events
The package dispatches a StreamVideoProcessed event whenever a webhook is received. You can listen for this event in your application:
// In a service provider or event subscriber:
use Modularavel\CloudflareStreamVideo\Events\StreamVideoProcessed;
Event::listen(StreamVideoProcessed::class, function (StreamVideoProcessed $event) {
// $event->eventType — e.g. "video.ready", "video.error"
// $event->videoUid — the Cloudflare video UID
// $event->payload — the full raw webhook payload
if ($event->eventType === 'video.ready') {
// Video encoding completed — update your database, notify users, etc.
$video = Video::where('cloudflare_uid', $event->videoUid)->first();
if ($video) {
$video->update(['status' => 'ready']);
}
}
if ($event->eventType === 'video.error') {
// Video encoding failed — log the error, notify admins, etc.
Log::error('Cloudflare Stream encoding failed for video: ' . $event->videoUid, [
'payload' => $event->payload,
]);
}
});
Webhook Payload Structure
Cloudflare Stream sends webhook payloads with the following structure:
{
"event_type": "video.ready",
"result": {
"uid": "cab807e0c477d01baq20f66c3d1dfc26cf",
"status": {
"state": "ready",
"pctComplete": "100"
},
"meta": { "name": "My Video" }
}
}
Webhook Signature Verification
The webhook controller verifies incoming requests using HMAC-SHA256 signatures. The signature is sent in the X-Cloudflare-Signature header as a hex-encoded HMAC digest of the raw request body.
If no CLOUDFLARE_STREAM_WEBHOOK_SECRET is configured, signature verification is skipped (not recommended for production).
Webhook Database Table
The package includes a migration that creates the cloudflare_stream_webhooks table for auditing webhook deliveries:
php artisan vendor:publish --tag="cloudflare-stream-video-migrations"
php artisan migrate
The table stores:
event_type— the Cloudflare Stream event typevideo_uid— the video identifier from the payloadpayload— the raw JSON payload (nullable)processed— boolean flag indicating whether the background job has processed the webhook
Blade Player Component
The package includes a responsive Blade component for embedding Cloudflare Stream videos.
Basic Usage
{{-- Simple unsigned embed --}}
<x-cloudflare-stream-player :videoUid="$video->uid" />
Signed Mode
{{-- Signed embed with expiry --}}
<x-cloudflare-stream-player
:videoUid="$video->uid"
:signed="true"
:expiresAt="now()->addHours(2)"
/>
Custom Dimensions and Styling
<x-cloudflare-stream-player
:videoUid="$video->uid"
width="100%"
height="400px"
class="my-custom-player"
/>
The component renders a responsive iframe wrapper with the Cloudflare Stream embed player. When signed is true, the component generates a signed embed URL using the configured signing key.
Artisan Commands
Placeholder Command
php artisan cloudflare-stream-video:placeholder
A basic command that confirms the package is installed and working.
Import Video from URL
php artisan cloudflare-stream-video:import https://example.com/video.mp4
# With metadata
php artisan cloudflare-stream-video:import https://example.com/video.mp4 \
--meta="name=My Video" \
--meta="source=laravel"
Sync Videos
php artisan cloudflare-stream-video:sync
Syncs videos from Cloudflare Stream with your local database.
Generate Signed URL
php artisan cloudflare-stream-video:sign video-uid-123
# With custom expiry
php artisan cloudflare-stream-video:sign video-uid-123 --expires=2030-01-01T00:00:00Z
# With details output
php artisan cloudflare-stream-video:sign video-uid-123 --details
Watermark Management
# Create a watermark
php artisan cloudflare-stream-video:watermark create \
--name="Brand Logo" \
--url="https://example.com/watermark.png" \
--position="bottom-right" \
--opacity=0.8
# List all watermarks
php artisan cloudflare-stream-video:watermark list
# Get a watermark by UID
php artisan cloudflare-stream-video:watermark get --watermark-uid=wm-brand-123
# Update a watermark
php artisan cloudflare-stream-video:watermark update \
--watermark-uid=wm-brand-123 \
--opacity=0.5
# Delete a watermark
php artisan cloudflare-stream-video:watermark delete --watermark-uid=wm-brand-123
# Apply a watermark to a video
php artisan cloudflare-stream-video:watermark apply \
--video-uid=cab807e0c477d01baq20f66c3d1dfc26cf \
--watermark-uid=wm-brand-123
# Remove a watermark from a video
php artisan cloudflare-stream-video:watermark remove \
--video-uid=cab807e0c477d01baq20f66c3d1dfc26cf
🧪 Testes
O pacote conta com 86 testes e 150 assertions usando Pest PHP:
# Rodar o conjunto completo de testes
composer test
# Rodar apenas testes unitários
composer test:unit
# Rodar verificação de estilo (Pint)
composer lint:check
# Rodar análise estática (PHPStan)
composer analyse
📚 Contribute
We welcome your contributions! Please check out our contributing guide for details.
🔒 Security Policy
Report security vulnerabilities responsibly through our security policy.
👨💻 Author
📄 License
This project is licensed under the MIT License — see the LICENSE.md file for details.
✨ Support
If you've found this package useful, please ⭐ the repository to show your support!