modularavel/cloudflare-stream-video

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.

Maintainers

Package info

github.com/modularavel/cloudflare-stream-video

pkg:composer/modularavel/cloudflare-stream-video

Transparency log

Fund package maintenance!

modularavel

Statistics

Installs: 2

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 3

0.1.0 2026-07-28 06:01 UTC

This package is auto-updated.

Last update: 2026-08-04 12:14:10 UTC


README

Cloudflare Stream Video Package Banner

modularavel/cloudflare-stream-video

Packagist PHP from Packagist Laravel versions GitHub Workflow Status (main) Total Downloads

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 videos
  • cloudflare_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

  1. Configure the webhook secret in your .env:
CLOUDFLARE_STREAM_WEBHOOK_SECRET=your-webhook-secret
  1. Configure the webhook route (optional, defaults to cloudflare-stream-video/webhook):
CLOUDFLARE_STREAM_WEBHOOK_ROUTE=cloudflare-stream-video/webhook
  1. 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 type
  • video_uid — the video identifier from the payload
  • payload — 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

Cloudflare Stream Video Package Tests

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

| | --- | |

Casimiro Rocha
Casimiro Rocha
📧 contato@crsistemas.dev.br

|

📄 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!

Made with ❤️ by Modularavel

Version 0.1.0