PHPackages                             ekstremedia/laravel-youtube - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [File &amp; Storage](/categories/file-storage)
4. /
5. ekstremedia/laravel-youtube

ActiveLibrary[File &amp; Storage](/categories/file-storage)

ekstremedia/laravel-youtube
===========================

A modern Laravel package for YouTube API v3 integration with OAuth2, automatic token refresh, and beautiful admin panel

v1.0.0(7mo ago)1730[3 issues](https://github.com/ekstremedia/laravel-youtube/issues)[3 PRs](https://github.com/ekstremedia/laravel-youtube/pulls)MITPHPPHP ^8.2CI passing

Since Nov 11Pushed 4mo agoCompare

[ Source](https://github.com/ekstremedia/laravel-youtube)[ Packagist](https://packagist.org/packages/ekstremedia/laravel-youtube)[ RSS](/packages/ekstremedia-laravel-youtube/feed)WikiDiscussions main Synced today

READMEChangelog (1)Dependencies (11)Versions (7)Used By (0)

Laravel YouTube Package
=======================

[](#laravel-youtube-package)

[![Latest Version on Packagist](https://camo.githubusercontent.com/6a5dc2c06461b360d711eba950645f62e4c5d8d80005dc2b587d0e1363ac3d8c/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f656b737472656d656469612f6c61726176656c2d796f75747562652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ekstremedia/laravel-youtube)[![Tests](https://github.com/ekstremedia/laravel-youtube/actions/workflows/tests.yml/badge.svg)](https://github.com/ekstremedia/laravel-youtube/actions/workflows/tests.yml)[![Total Downloads](https://camo.githubusercontent.com/b73a12baae1760b6a252e7ebb8f097ccb18aa2e06f3aef932766f11faaee8827/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f656b737472656d656469612f6c61726176656c2d796f75747562652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ekstremedia/laravel-youtube)[![License](https://camo.githubusercontent.com/775acd1d623c04ead33e151b0e12ee2a537007b509942c17183b94b047b3ecae/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f656b737472656d656469612f6c61726176656c2d796f75747562652e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/ekstremedia/laravel-youtube)[![Laravel](https://camo.githubusercontent.com/0243451dc8241bd980824bcfa0c4bf6d508810b35bd8327bc2ba7fda2d43cec1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d313125324225323025374325323031322532422d726564)](https://camo.githubusercontent.com/0243451dc8241bd980824bcfa0c4bf6d508810b35bd8327bc2ba7fda2d43cec1/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d313125324225323025374325323031322532422d726564)[![PHP](https://camo.githubusercontent.com/187240af044d09d5b14a1d9d9ebdf3f7a993e4c7bc09bdb46b4ba661a891bf5b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c7565)](https://camo.githubusercontent.com/187240af044d09d5b14a1d9d9ebdf3f7a993e4c7bc09bdb46b4ba661a891bf5b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e322532422d626c7565)

A comprehensive Laravel package for YouTube API v3 integration with OAuth2 authentication, automatic token refresh, upload management, and extensive API coverage. Perfect for content creators, video platforms, and automated video upload systems.

🚀 Features
----------

[](#-features)

### Core Features

[](#core-features)

- 🔐 **OAuth2 Authentication** - Secure authentication with automatic token refresh
- 📹 **Video Management** - Complete CRUD operations for videos
- 📤 **Advanced Upload** - Chunked uploads with progress tracking &amp; 15+ metadata fields
- 📺 **Channel Management** - Designed for single-user/single-channel applications
- 🎨 **Thumbnail Management** - Custom thumbnail upload support
- 🎬 **Playlist Operations** - Create, manage, and organize video playlists
- 💬 **Caption/Subtitle Support** - Upload, manage, and download captions in multiple formats
- 📍 **Location &amp; Recording Details** - Geotag videos with coordinates and recording dates
- 🔄 **Queue Support** - Background video uploads via Laravel jobs
- 🎯 **Rate Limiting** - Built-in rate limiting to respect API quotas
- 📊 **Statistics Tracking** - View counts, likes, and engagement metrics
- 🌐 **Webhook Support** - Upload completion notifications
- 📅 **Scheduled Publishing** - Schedule videos for future publication

### Special Features

[](#special-features)

- 🥧 **Raspberry Pi Integration** - Optimized for automated uploads from IoT devices
- 🔄 **Auto Token Refresh** - Never worry about expired tokens
- 💾 **Database Storage** - Track all uploads and video metadata
- 🔒 **Encrypted Token Storage** - Secure storage of OAuth tokens
- 📝 **Comprehensive Logging** - Detailed logging for debugging
- 🧪 **Test Coverage** - Extensive test suite with Pest

📋 Requirements
--------------

[](#-requirements)

- PHP 8.2 or higher
- Laravel 11.0 or 12.0
- Google API credentials (OAuth 2.0 Client ID)
- Composer
- Database (MySQL, PostgreSQL, SQLite)

📦 Installation
--------------

[](#-installation)

### Step 1: Install via Composer

[](#step-1-install-via-composer)

```
composer require ekstremedia/laravel-youtube
```

### Step 2: Publish Configuration and Migrations

[](#step-2-publish-configuration-and-migrations)

```
# Publish configuration
php artisan vendor:publish --provider="EkstreMedia\LaravelYouTube\YouTubeServiceProvider" --tag="youtube-config"

# Publish migrations
php artisan vendor:publish --provider="EkstreMedia\LaravelYouTube\YouTubeServiceProvider" --tag="youtube-migrations"

# Optional: Publish views for authorization page
php artisan vendor:publish --provider="EkstreMedia\LaravelYouTube\YouTubeServiceProvider" --tag="youtube-views"
```

### Step 3: Configure Environment Variables

[](#step-3-configure-environment-variables)

Add the following to your `.env` file:

```
# Required: YouTube OAuth2 Credentials
YOUTUBE_CLIENT_ID=your-client-id-here
YOUTUBE_CLIENT_SECRET=your-client-secret-here
YOUTUBE_REDIRECT_URI=https://yourdomain.com/youtube/callback

# Optional: Authentication Page
YOUTUBE_AUTH_PAGE_PATH=youtube-authorize

# Optional: Upload Settings
YOUTUBE_UPLOAD_CHUNK_SIZE=10485760  # 10MB chunks
YOUTUBE_UPLOAD_TIMEOUT=3600         # 1 hour
YOUTUBE_UPLOAD_MAX_SIZE=137438953472 # 128GB (YouTube's max)

# Optional: Default Settings
YOUTUBE_DEFAULT_PRIVACY=private     # private, unlisted, public
YOUTUBE_DEFAULT_CATEGORY=22         # People & Blogs
YOUTUBE_DEFAULT_LANGUAGE=en

# Optional: Rate Limiting
YOUTUBE_RATE_LIMIT_ENABLED=true
YOUTUBE_RATE_LIMIT_PER_MINUTE=60
YOUTUBE_RATE_LIMIT_PER_HOUR=3000

# Optional: Logging
YOUTUBE_LOGGING_ENABLED=true
YOUTUBE_LOGGING_CHANNEL=youtube
YOUTUBE_LOGGING_LEVEL=info
```

### Step 4: Obtain Google API Credentials

[](#step-4-obtain-google-api-credentials)

1. Go to [Google Cloud Console](https://console.cloud.google.com)
2. Create a new project or select an existing one
3. Enable the **YouTube Data API v3**
4. Create OAuth 2.0 credentials:
    - Application type: Web application
    - Add authorized redirect URI: `https://yourdomain.com/youtube/callback`
5. Copy the Client ID and Client Secret to your `.env` file

### Step 5: Run Migrations

[](#step-5-run-migrations)

```
php artisan migrate
```

🎯 Quick Start
-------------

[](#-quick-start)

### Basic Usage

[](#basic-usage)

```
use EkstreMedia\LaravelYouTube\Facades\YouTube;

// Authenticate user (redirect to Google)
return redirect()->to(YouTube::getAuthUrl());

// After authentication, upload a video
$video = YouTube::forUser(auth()->id())
    ->uploadVideo(
        '/path/to/video.mp4',
        [
            'title' => 'My Amazing Video',
            'description' => 'This is a great video!',
            'tags' => ['laravel', 'youtube', 'api'],
            'category_id' => '22',
            'privacy_status' => 'public',
        ]
    );

echo "Video uploaded: " . $video->watch_url;
```

### Raspberry Pi Integration

[](#raspberry-pi-integration)

Perfect for automated timelapse or security camera uploads. The package is designed for single-user/single-channel applications.

#### Using the Service Without User Context

[](#using-the-service-without-user-context)

```
// Method 1: Use the default (most recent) active token
YouTube::usingDefault()->uploadVideo($file, $metadata);

// Method 2: Use a specific channel by ID
YouTube::forChannel('UCxxxxxxxxxx')->uploadVideo($file, $metadata);

// Method 3: For legacy support, specify user ID
YouTube::forUser($userId)->uploadVideo($file, $metadata);
```

#### Complete Raspberry Pi Example

[](#complete-raspberry-pi-example)

```
// In your Pi upload endpoint
use Ekstremedia\LaravelYouTube\Facades\YouTube;

Route::post('/api/pi/upload', function (Request $request) {
    $request->validate([
        'video' => 'required|file|mimes:mp4,avi,mov|max:5242880', // 5GB
        'camera_id' => 'required|string',
    ]);

    $file = $request->file('video');

    // Upload using default token (single-user mode)
    $video = YouTube::usingDefault()->uploadVideo($file, [
        'title' => "Pi Camera {$request->camera_id} - " . now()->format('Y-m-d H:i'),
        'description' => 'Automated timelapse from Raspberry Pi',
        'tags' => ['raspberry-pi', 'timelapse', $request->camera_id],
        'privacy_status' => 'unlisted',
    ]);

    return response()->json([
        'success' => true,
        'video_id' => $video->video_id,
        'watch_url' => $video->watch_url,
    ]);
});
```

#### From Raspberry Pi (Shell Script)

[](#from-raspberry-pi-shell-script)

```
#!/bin/bash
# On your Raspberry Pi

VIDEO_FILE="/home/pi/videos/timelapse_$(date +%Y%m%d_%H%M%S).mp4"
API_ENDPOINT="https://your-domain.com/api/pi/upload"
API_TOKEN="your-api-token"

# Upload to your Laravel API
curl -X POST $API_ENDPOINT \
  -H "Authorization: Bearer $API_TOKEN" \
  -F "video=@$VIDEO_FILE" \
  -F "camera_id=pi_camera_1"
```

#### One-Time Google OAuth Setup

[](#one-time-google-oauth-setup)

Visit the authorization page to connect your YouTube channel:

```
https://yourdomain.com/youtube-authorize

```

The page will:

1. Check if credentials are configured
2. Show your connected channel (if any)
3. Allow you to authorize/re-authorize with Google
4. Automatically refresh expired tokens

After authorization, all uploads will use this token automatically.

📚 Comprehensive Documentation
-----------------------------

[](#-comprehensive-documentation)

### Authentication Page

[](#authentication-page)

The package includes a simple authentication page for connecting your YouTube channel:

**Access the page:** `https://yourdomain.com/youtube-authorize` (configurable)

**Features:**

- Check if credentials are configured
- View connected YouTube channel
- One-click authorize/re-authorize
- Automatic token refresh
- Clean, simple interface
- **Protected by authentication** - Requires logged-in user by default

**Security:**

By default, the authorization page is protected with the `auth` middleware. Only logged-in users can access it.

To customize the middleware protection, publish and edit the config file:

```
// config/youtube.php
'routes' => [
    'auth_page' => [
        'middleware' => ['web', 'auth'], // Default: requires login

        // Examples:
        // 'middleware' => ['web', 'auth:admin'],           // Admin guard
        // 'middleware' => ['web', 'can:manage-youtube'],  // Laravel Gate
        // 'middleware' => ['web'],                         // No authentication
    ],
],
```

**Configuration:**

```
YOUTUBE_AUTH_PAGE_PATH=youtube-authorize
```

**Usage in your app:**

```
// Link to authentication page
Connect YouTube

// Or use the configured path
Authorize YouTube
```

The page shows:

1. Configuration status (credentials check)
2. Connected channel information
3. Authorization button
4. Token expiration and refresh status

### Authentication &amp; Token Management

[](#authentication--token-management)

#### OAuth Flow

[](#oauth-flow)

```
use EkstreMedia\LaravelYouTube\Services\AuthService;

$authService = app(AuthService::class);

// Generate OAuth URL with state for CSRF protection
$state = Str::random(40);
session(['youtube_oauth_state' => $state]);
$authUrl = $authService->getAuthUrl($state);

// In callback handler
if (request('state') !== session('youtube_oauth_state')) {
    abort(403, 'Invalid state');
}

// Exchange code for tokens
$tokens = $authService->exchangeCode(request('code'));
```

#### Token Storage &amp; Management

[](#token-storage--management)

```
use EkstreMedia\LaravelYouTube\Services\TokenManager;

$tokenManager = app(TokenManager::class);

// Store tokens after OAuth
$token = $tokenManager->storeToken(
    $tokens,
    $channelInfo,
    auth()->id()
);

// Get active token for user
$token = $tokenManager->getActiveToken(auth()->id());

// Check if refresh needed (within 5 minutes of expiry)
if ($tokenManager->needsRefresh($token)) {
    $newTokens = $authService->refreshAccessToken($token->refresh_token);
    $tokenManager->updateToken($token, $newTokens);
}

// Handle multiple channels
$tokens = $tokenManager->getUserTokens(auth()->id());
foreach ($tokens as $token) {
    echo "Channel: {$token->channel_title}\n";
}
```

### Video Management

[](#video-management)

#### Upload Videos

[](#upload-videos)

```
use EkstreMedia\LaravelYouTube\Facades\YouTube;

// Simple upload
$video = YouTube::forUser(auth()->id())->uploadVideo(
    $request->file('video'),
    [
        'title' => 'My Video',
        'description' => 'Video description',
        'tags' => ['tag1', 'tag2'],
        'category_id' => '22', // People & Blogs
        'privacy_status' => 'private', // private, unlisted, public
    ]
);

// Advanced upload with all metadata options
$video = YouTube::forUser(auth()->id())->uploadVideo(
    '/path/to/large-video.mp4',
    [
        // Basic metadata
        'title' => 'Large Video Upload',
        'description' => 'Testing chunked upload',
        'tags' => ['large', 'chunked'],
        'category_id' => '22',

        // Privacy & Status
        'privacy_status' => 'private',
        'made_for_kids' => false,
        'self_declared_made_for_kids' => false,
        'embeddable' => true,
        'public_stats_viewable' => true,
        'publish_at' => '2024-12-31T12:00:00Z', // Scheduled publishing

        // License & Language
        'license' => 'creativeCommon',
        'default_language' => 'en',
        'default_audio_language' => 'en-US',

        // Recording details (great for travel vlogs, security cameras)
        'recording_date' => '2024-01-15T10:00:00Z',
        'location' => [
            'latitude' => 59.9139,
            'longitude' => 10.7522,
            'altitude' => 100.0,
            'description' => 'Oslo, Norway',
        ],

        // Custom thumbnail
        'thumbnail' => '/path/to/thumbnail.jpg',
    ],
    [
        'chunk_size' => 50 * 1024 * 1024, // 50MB chunks
        'notify_url' => 'https://yourapp.com/webhook',
        'progress_callback' => function ($uploaded, $total) {
            $percent = round(($uploaded / $total) * 100);
            Log::info("Upload progress: {$percent}%");
        }
    ]
);
```

#### Manage Videos

[](#manage-videos)

```
// Get user's videos
$videos = YouTube::forUser(auth()->id())->getVideos([
    'maxResults' => 50,
    'order' => 'date', // date, rating, relevance, title, viewCount
    'type' => 'video',
    'videoDefinition' => 'high', // any, high, standard
    'videoDuration' => 'medium', // short (20min)
]);

// Get single video details
$video = YouTube::forUser(auth()->id())->getVideo('video-id', [
    'snippet',
    'contentDetails',
    'statistics',
    'status',
    'processingDetails'
]);

// Update video metadata
$updated = YouTube::forUser(auth()->id())->updateVideo('video-id', [
    'title' => 'Updated Title',
    'description' => 'Updated description',
    'tags' => ['new', 'tags'],
    'category_id' => '24',
    'privacy_status' => 'public',
]);

// Delete video
YouTube::forUser(auth()->id())->deleteVideo('video-id');
```

### Channel Management

[](#channel-management)

```
// Get channel info
$channel = YouTube::forUser(auth()->id())->getChannel([
    'snippet',
    'contentDetails',
    'statistics',
    'brandingSettings',
    'contentOwnerDetails',
    'localizations',
    'status',
    'topicDetails'
]);

// Get channel videos
$videos = YouTube::forUser(auth()->id())->getChannelVideos('channel-id', [
    'maxResults' => 50,
    'order' => 'date',
]);

// Switch between multiple channels
$tokens = YouTubeToken::where('user_id', auth()->id())->get();
foreach ($tokens as $token) {
    $youtube = YouTube::withToken($token);
    $channel = $youtube->getChannel();
    echo "Channel: {$channel['title']} ({$channel['subscriberCount']} subscribers)\n";
}
```

### Playlist Management

[](#playlist-management)

```
// Create a new playlist
$playlist = YouTube::usingDefault()->createPlaylist('My Playlist', [
    'description' => 'Collection of my favorite videos',
    'privacy_status' => 'public', // private, public, unlisted
    'tags' => ['favorites', 'collection'],
]);

// Get all playlists
$result = YouTube::usingDefault()->getPlaylists();
foreach ($result['playlists'] as $playlist) {
    echo "{$playlist['title']} - {$playlist['item_count']} videos\n";
}

// Add video to playlist
YouTube::usingDefault()->addVideoToPlaylist(
    'video-id',
    'playlist-id',
    0 // Position (0 = first)
);

// Get videos in a playlist
$result = YouTube::usingDefault()->getPlaylistVideos('playlist-id');
foreach ($result['videos'] as $video) {
    echo "{$video['title']} (position: {$video['position']})\n";
}

// Update playlist
YouTube::usingDefault()->updatePlaylist('playlist-id', [
    'title' => 'Updated Title',
    'privacy_status' => 'public',
]);

// Delete playlist
YouTube::usingDefault()->deletePlaylist('playlist-id');
```

### Caption/Subtitle Management

[](#captionsubtitle-management)

```
// Upload captions (supports SRT, VTT, TTML, SBV)
$caption = YouTube::usingDefault()->uploadCaption(
    'video-id',
    'en', // Language code
    '/path/to/captions.srt',
    [
        'name' => 'English Subtitles',
        'is_draft' => false,
    ]
);

// List all captions for a video
$captions = YouTube::usingDefault()->getCaptions('video-id');
foreach ($captions as $caption) {
    echo "{$caption['name']} ({$caption['language']})\n";
}

// Update caption metadata or file
YouTube::usingDefault()->updateCaption(
    'caption-id',
    ['name' => 'Updated English'],
    '/path/to/new-captions.srt' // Optional: new file
);

// Download captions in different formats
$srt = YouTube::usingDefault()->downloadCaption('caption-id', 'srt');
$vtt = YouTube::usingDefault()->downloadCaption('caption-id', 'vtt');
file_put_contents('captions.srt', $srt);

// Delete captions
YouTube::usingDefault()->deleteCaption('caption-id');
```

### Background Jobs &amp; Queues

[](#background-jobs--queues)

You can implement your own background job processing. Here's an example:

```
// Create your own job
namespace App\Jobs;

use Ekstremedia\LaravelYouTube\Facades\YouTube;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;

class UploadYouTubeVideo implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    public function __construct(
        public string $videoPath,
        public array $metadata
    ) {}

    public function handle(): void
    {
        $video = YouTube::usingDefault()->uploadVideo(
            $this->videoPath,
            $this->metadata
        );

        // Clean up temporary file
        unlink($this->videoPath);
    }
}

// Dispatch the job
UploadYouTubeVideo::dispatch(
    videoPath: '/path/to/video.mp4',
    metadata: [
        'title' => 'Queued Upload',
        'description' => 'Uploaded via queue',
        'tags' => ['queued', 'background'],
        'privacy_status' => 'private',
    ]
)->onQueue('media');
```

### Advanced Features

[](#advanced-features)

#### Live Streaming

[](#live-streaming)

```
// Create live broadcast
$broadcast = YouTube::forUser(auth()->id())->createLiveBroadcast([
    'title' => 'Live Stream',
    'description' => 'Live streaming event',
    'scheduled_start_time' => now()->addHour(),
    'scheduled_end_time' => now()->addHours(2),
    'privacy_status' => 'public',
]);

// Create live stream
$stream = YouTube::forUser(auth()->id())->createLiveStream([
    'title' => 'Stream',
    'description' => 'Stream description',
    'cdn' => [
        'frameRate' => '30fps',
        'resolution' => '1080p',
        'ingestionType' => 'rtmp',
    ],
]);

// Bind stream to broadcast
YouTube::forUser(auth()->id())->bindBroadcastToStream(
    $broadcast['id'],
    $stream['id']
);
```

#### Comments Management

[](#comments-management)

```
// Get video comments
$comments = YouTube::forUser(auth()->id())->getVideoComments('video-id', [
    'maxResults' => 100,
    'order' => 'time', // time, relevance
    'textFormat' => 'plainText', // plainText, html
]);

// Reply to comment
YouTube::forUser(auth()->id())->replyToComment('comment-id', 'Thank you for watching!');

// Moderate comments
YouTube::forUser(auth()->id())->setCommentModerationStatus('comment-id', 'published'); // heldForReview, published, rejected
```

#### Analytics Integration

[](#analytics-integration)

```
// Get video analytics (requires YouTube Analytics API)
$analytics = YouTube::forUser(auth()->id())->getVideoAnalytics('video-id', [
    'metrics' => 'views,estimatedMinutesWatched,averageViewDuration',
    'dimensions' => 'day',
    'startDate' => now()->subDays(30)->format('Y-m-d'),
    'endDate' => now()->format('Y-m-d'),
]);
```

🧪 Testing
---------

[](#-testing)

The package includes comprehensive test coverage:

```
# Run all tests
composer test

# Run specific test suite
vendor/bin/pest --filter="Upload"

# Run with coverage
composer test-coverage
```

Test categories:

- Unit tests for configuration and models
- Feature tests for services and API endpoints
- Integration tests for OAuth flow
- Upload tests including chunked uploads
- Token management and refresh tests
- Rate limiting and authentication tests

🔧 Configuration
---------------

[](#-configuration)

Full configuration options in `config/youtube.php`:

```
return [
    'credentials' => [
        'client_id' => env('YOUTUBE_CLIENT_ID'),
        'client_secret' => env('YOUTUBE_CLIENT_SECRET'),
        'redirect_uri' => env('YOUTUBE_REDIRECT_URI', '/youtube/callback'),
    ],

    'scopes' => [
        'https://www.googleapis.com/auth/youtube',
        'https://www.googleapis.com/auth/youtube.upload',
        'https://www.googleapis.com/auth/youtube.readonly',
        'https://www.googleapis.com/auth/youtube.force-ssl',
        'https://www.googleapis.com/auth/youtubepartner',
        'https://www.googleapis.com/auth/youtubepartner-channel-audit',
    ],

    'admin' => [
        'enabled' => env('YOUTUBE_ADMIN_ENABLED', true),
        'prefix' => env('YOUTUBE_ADMIN_PREFIX', 'youtube-admin'),
        'middleware' => ['web'],
        'auth_middleware' => ['auth'],
    ],

    'routes' => [
        'api' => [
            'enabled' => env('YOUTUBE_API_ENABLED', true),
            'prefix' => env('YOUTUBE_API_PREFIX', 'youtube'),
            'middleware' => ['api'],
            'api_middleware' => ['auth:sanctum', 'throttle:60,1'],
        ],
    ],

    'token' => [
        'driver' => 'database',
        'table' => 'youtube_tokens',
        'cache_key' => 'youtube.token.',
        'cache_ttl' => env('YOUTUBE_TOKEN_CACHE_TTL', 3600),
    ],

    'upload' => [
        'chunk_size' => env('YOUTUBE_UPLOAD_CHUNK_SIZE', 1024 * 1024), // 1MB
        'timeout' => env('YOUTUBE_UPLOAD_TIMEOUT', 3600),
        'max_file_size' => env('YOUTUBE_UPLOAD_MAX_SIZE', 128 * 1024 * 1024 * 1024), // 128GB
        'temp_path' => env('YOUTUBE_UPLOAD_TEMP_PATH', storage_path('app/youtube-uploads')),
    ],

    'defaults' => [
        'privacy_status' => env('YOUTUBE_DEFAULT_PRIVACY', 'private'),
        'category_id' => env('YOUTUBE_DEFAULT_CATEGORY', '22'),
        'language' => env('YOUTUBE_DEFAULT_LANGUAGE', 'en'),
    ],

    'rate_limiting' => [
        'enabled' => env('YOUTUBE_RATE_LIMIT_ENABLED', true),
        'max_requests_per_minute' => env('YOUTUBE_RATE_LIMIT_PER_MINUTE', 60),
        'max_requests_per_hour' => env('YOUTUBE_RATE_LIMIT_PER_HOUR', 3000),
    ],

    'logging' => [
        'enabled' => env('YOUTUBE_LOGGING_ENABLED', true),
        'channel' => env('YOUTUBE_LOGGING_CHANNEL', 'youtube'),
        'level' => env('YOUTUBE_LOGGING_LEVEL', 'info'),
    ],
];
```

🛠️ Console Commands
-------------------

[](#️-console-commands)

```
# Refresh expiring tokens
php artisan youtube:refresh-tokens
php artisan youtube:refresh-tokens --token-id=1
php artisan youtube:refresh-tokens --user-id=1 --force

# Clean up expired tokens
php artisan youtube:clear-expired-tokens
php artisan youtube:clear-expired-tokens --days=30 --dry-run

# Sync video statistics
php artisan youtube:sync-videos
php artisan youtube:sync-videos --user-id=1
php artisan youtube:sync-videos --video-id=abc123
```

🔄 Scheduled Tasks
-----------------

[](#-scheduled-tasks)

Add to your `app/Console/Kernel.php`:

```
protected function schedule(Schedule $schedule)
{
    // Automatically refresh expiring tokens
    $schedule->command('youtube:refresh-tokens')->hourly();

    // Clean up expired tokens daily
    $schedule->command('youtube:clear-expired-tokens')->daily();

    // Sync video statistics every 6 hours
    $schedule->command('youtube:sync-videos')->everySixHours();
}
```

🚨 Error Handling
----------------

[](#-error-handling)

The package provides specific exception types:

```
use EkstreMedia\LaravelYouTube\Exceptions\{
    YouTubeException,
    YouTubeAuthException,
    UploadException,
    TokenException,
    QuotaExceededException
};

try {
    $video = YouTube::forUser($userId)->uploadVideo($file, $metadata);
} catch (QuotaExceededException $e) {
    // Handle quota exceeded
    Log::error("YouTube quota exceeded: " . $e->getMessage());
    // Retry after reset
} catch (UploadException $e) {
    // Handle upload failure
    Log::error("Upload failed: " . $e->getMessage());
} catch (TokenException $e) {
    // Handle token issues
    return redirect()->route('youtube.auth');
} catch (YouTubeException $e) {
    // Handle general YouTube API errors
    Log::error("YouTube API error: " . $e->getYouTubeError());
}
```

🔐 Security
----------

[](#-security)

- OAuth tokens are encrypted using Laravel's encryption
- CSRF protection on OAuth flow
- Rate limiting on API endpoints
- Scoped access control
- Automatic token rotation
- Secure webhook signatures

📊 Events
--------

[](#-events)

The package dispatches Laravel events:

```
// Listen for events in EventServiceProvider
protected $listen = [
    \EkstreMedia\LaravelYouTube\Events\VideoUploaded::class => [
        \App\Listeners\ProcessUploadedVideo::class,
    ],
    \EkstreMedia\LaravelYouTube\Events\TokenRefreshed::class => [
        \App\Listeners\LogTokenRefresh::class,
    ],
    \EkstreMedia\LaravelYouTube\Events\UploadFailed::class => [
        \App\Listeners\NotifyUploadFailure::class,
    ],
];
```

🤝 Contributing
--------------

[](#-contributing)

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.

🧪 Testing
---------

[](#-testing-1)

```
composer test
composer test-coverage
composer format
composer analyse
```

📝 Changelog
-----------

[](#-changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

🔒 Security
----------

[](#-security-1)

If you discover any security-related issues, please email  instead of using the issue tracker.

📜 License
---------

[](#-license)

The MIT License (MIT). Please see [License File](LICENSE) for more information.

👥 Credits
---------

[](#-credits)

- [Terje Nesthus](https://github.com/terjenesthus)
- [All Contributors](../../contributors)

🙏 Acknowledgments
-----------------

[](#-acknowledgments)

- Thanks to Google for the YouTube Data API
- Laravel framework for the excellent foundation
- The open-source community for inspiration

📞 Support
---------

[](#-support)

- 📧 Email:
- 🐛 Issues: [GitHub Issues](https://github.com/ekstremedia/laravel-youtube/issues)
- 💬 Discussions: [GitHub Discussions](https://github.com/ekstremedia/laravel-youtube/discussions)
- 📖 Documentation: [Full Docs](https://ekstremedia.github.io/laravel-youtube)

---

Made with ❤️ by [Ekstre Media](https://ekstremedia.no)

###  Health Score

34

—

LowBetter than 75% of packages

Maintenance49

Moderate activity, may be stable

Popularity18

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity52

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 88.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

234d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/81c14fb7d455e136320a619693165f18eb4f220301b44512cdc083fa9b599bf7?d=identicon)[terjenesthus](/maintainers/terjenesthus)

---

Top Contributors

[![ekstremedia](https://avatars.githubusercontent.com/u/10083373?v=4)](https://github.com/ekstremedia "ekstremedia (40 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (5 commits)")

---

Tags

apilaravelvideoyoutubeoauth2upload

###  Code Quality

TestsPest

Static AnalysisPHPStan

Code StyleLaravel Pint

Type Coverage Yes

### Embed Badge

![Health badge](/badges/ekstremedia-laravel-youtube/health.svg)

```
[![Health](https://phpackages.com/badges/ekstremedia-laravel-youtube/health.svg)](https://phpackages.com/packages/ekstremedia-laravel-youtube)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.6k29.9M147](/packages/laravel-cashier)[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k15.1M132](/packages/laravel-pulse)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9762.4M131](/packages/roots-acorn)[api-platform/laravel

API Platform support for Laravel

58171.6k14](/packages/api-platform-laravel)[pressbooks/pressbooks

Pressbooks is an open source book publishing tool built on a WordPress multisite platform. Pressbooks outputs books in multiple formats, including PDF, EPUB, web, and a variety of XML flavours, using a theming/templating system, driven by CSS.

45444.2k1](/packages/pressbooks-pressbooks)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
