arbet/laravel-sora

Laravel package for OpenAI Video API integration

Installs: 3

Dependents: 0

Suggesters: 0

Security: 0

Stars: 0

Watchers: 0

Forks: 0

Open Issues: 0

Language:HTML

pkg:composer/arbet/laravel-sora

v1.0.1 2025-10-12 06:53 UTC

This package is auto-updated.

Last update: 2025-11-28 11:10:00 UTC


README

Latest Version on Packagist Total Downloads

Easy integration of OpenAI Video API with Laravel applications. This package provides a simple and elegant way to interact with OpenAI's Video API endpoints.

Features

  • 🎥 Generate videos from text prompts
  • 🖼️ Generate videos with reference images
  • 🎬 Remix existing videos with new prompts
  • 📤 Upload video files to OpenAI
  • 🔍 Analyze video content with AI
  • 📊 Check video generation status
  • 💾 Download generated videos
  • 🎯 Laravel-friendly facade interface
  • ⚡ Built on Guzzle HTTP client
  • 🛡️ Custom exception handling

Requirements

  • PHP 8.1 or higher
  • Laravel 10.x or 11.x
  • OpenAI API key

Installation

You can install the package via composer:

composer require arbet/laravel-sora

Configuration

Publish Configuration File

Publish the configuration file to your application:

php artisan vendor:publish --tag=openai-video-config

This will create a config/openai-video.php file in your application.

Environment Variables

Add your OpenAI API key to your .env file:

OPENAI_API_KEY=your-openai-api-key-here
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_TIMEOUT=120
OPENAI_VIDEO_MODEL=gpt-4o

Usage

Using the Facade

use Arbet\OpenAIVideo\Facades\OpenAIVideo;

// Generate a video from a text prompt
$result = OpenAIVideo::generate('A serene sunset over the ocean', [
    'size' => '1920x1080',
    'duration' => 10,
]);

// Upload a video file
$upload = OpenAIVideo::upload('/path/to/video.mp4', 'assistants');

// Analyze video content
$analysis = OpenAIVideo::analyze(
    'https://example.com/video.mp4',
    'Describe what is happening in this video'
);

// Check generation status
$status = OpenAIVideo::getStatus($result['id']);

// Download generated video
$path = OpenAIVideo::download($result['url'], 'videos/generated.mp4');

Using Dependency Injection

use Arbet\OpenAIVideo\OpenAIVideo;

class VideoController extends Controller
{
    public function __construct(
        protected OpenAIVideo $openAIVideo
    ) {}

    public function generate(Request $request)
    {
        try {
            $result = $this->openAIVideo->generate($request->input('prompt'), [
                'size' => $request->input('size', '1920x1080'),
            ]);

            return response()->json($result);
        } catch (\Arbet\OpenAIVideo\Exceptions\OpenAIVideoException $e) {
            return response()->json([
                'error' => $e->getMessage()
            ], 500);
        }
    }
}

Available Methods

generate(string $prompt, array $options = []): array

Generate a video from a text prompt.

$result = OpenAIVideo::generate('A cat playing piano', [
    'model' => 'gpt-4o',
    'size' => '1920x1080',
]);

Generating Videos with Reference Images

You can pass a reference image to guide the video generation by including the image parameter in the options array. The image parameter should contain the file ID of an image that has been uploaded to OpenAI.

Step 1: Upload the reference image

// Upload an image file (use 'vision' as the purpose)
$imageUpload = OpenAIVideo::upload('/path/to/reference-image.jpg', 'vision');
// Returns: ['id' => 'file-abc123', 'object' => 'file', 'purpose' => 'vision', ...]

Step 2: Generate video with the reference image

// Pass the uploaded image file ID in the options array
$result = OpenAIVideo::generate('Animate this image with flowing water', [
    'image' => $imageUpload['id'],
    'size' => '1920x1080',
    'duration' => 10,
]);

Complete Example:

use Arbet\OpenAIVideo\Facades\OpenAIVideo;

// Upload reference image
$imageUpload = OpenAIVideo::upload(storage_path('app/reference-image.jpg'), 'vision');

// Generate video using the reference image
$result = OpenAIVideo::generate(
    'Transform this scene into a vibrant sunset with moving clouds',
    [
        'image' => $imageUpload['id'],
        'size' => '1920x1080',
        'duration' => 15,
    ]
);

// Check the generation status
$status = OpenAIVideo::getStatus($result['id']);

// Download when ready
if ($status['status'] === 'completed') {
    $path = OpenAIVideo::download($status['url'], 'videos/generated.mp4');
}

Supported Image Formats:

  • JPEG (.jpg, .jpeg)
  • PNG (.png)
  • GIF (.gif)
  • WebP (.webp)

Supported Parameters

The generate() method supports all parameters from the OpenAI Video API through the $options array:

prompt (string, required)

The text description of the video you want to generate. Passed as the first argument.

OpenAIVideo::generate('A serene sunset over the ocean')

model (string, optional)

The AI model to use for video generation. Defaults to the value in your config file.

OpenAIVideo::generate('A sunset', [
    'model' => 'sora-1.0',
])

Available models:

  • sora-1.0 (or check OpenAI's documentation for latest models)

size (string, optional)

The dimensions of the generated video.

OpenAIVideo::generate('A sunset', [
    'size' => '1920x1080',
])

Common sizes:

  • 1920x1080 (Full HD)
  • 1280x720 (HD)
  • 720x480 (SD)

duration (integer, optional)

The duration of the video in seconds.

OpenAIVideo::generate('A sunset', [
    'duration' => 15,  // 15 seconds
])

image (string, optional)

File ID of a reference image to guide the video generation. The image must first be uploaded using the upload() method.

$imageUpload = OpenAIVideo::upload('/path/to/reference.jpg', 'vision');

OpenAIVideo::generate('Animate this scene', [
    'image' => $imageUpload['id'],
])

See Generating Videos with Reference Images for complete examples.

Complete Parameter Example

use Arbet\OpenAIVideo\Facades\OpenAIVideo;

// Upload reference image (optional)
$imageUpload = OpenAIVideo::upload(
    storage_path('app/reference.jpg'), 
    'vision'
);

// Generate video with all parameters
$result = OpenAIVideo::generate(
    'Create an epic cinematic scene from this image',
    [
        'model' => 'sora-1.0',
        'image' => $imageUpload['id'],
        'size' => '1920x1080',
        'duration' => 20,
    ]
);

// The result contains:
// - 'id' => Generation ID
// - 'status' => 'processing' | 'completed' | 'failed'
// - Other metadata from OpenAI

remix(string $videoId, string $prompt, array $options = []): array

Remix an existing completed video by applying a new text prompt to transform it.

Important: The video must be in completed status before you can remix it.

// First, generate a video
$result = OpenAIVideo::generate('A person walking in a park');

// Wait for it to complete (check status)
$status = OpenAIVideo::getStatus($result['id']);

// Once completed, remix it with a new prompt
if ($status['status'] === 'completed') {
    $remix = OpenAIVideo::remix(
        $status['id'],
        'Transform into a vintage 1980s style with neon colors'
    );
}

Response Structure

The method returns a video job object with the following structure:

[
    'id' => 'video_abc123',                    // Unique identifier for the remix job
    'object' => 'video',                        // Always 'video'
    'status' => 'processing',                   // 'processing', 'completed', or 'failed'
    'created_at' => 1634567890,                // Unix timestamp (seconds)
    'model' => 'sora-1.0',                     // The video generation model used
    'size' => '1920x1080',                     // Resolution of the video
    'seconds' => '10',                         // Duration in seconds
    'progress' => 45,                          // Approximate completion percentage
    'remixed_from_video_id' => 'video_xyz789',// ID of the source video
    'completed_at' => null,                    // Unix timestamp when finished (if completed)
    'expires_at' => 1634654290,               // Unix timestamp when assets expire
    'error' => null,                           // Error object (if failed)
]

Complete Example

use Arbet\OpenAIVideo\Facades\OpenAIVideo;

// Step 1: Generate original video
$original = OpenAIVideo::generate('A serene sunset over the ocean', [
    'size' => '1920x1080',
    'duration' => 10,
]);

echo "Original video ID: {$original['id']}\n";

// Step 2: Poll for completion
do {
    sleep(10); // Wait 10 seconds between checks
    $status = OpenAIVideo::getStatus($original['id']);
    echo "Status: {$status['status']} - Progress: {$status['progress']}%\n";
} while ($status['status'] === 'processing');

// Step 3: Remix the completed video
if ($status['status'] === 'completed') {
    $remix = OpenAIVideo::remix(
        $status['id'],
        'Apply vintage film effect with enhanced warm colors'
    );
    
    echo "Remix job ID: {$remix['id']}\n";
    echo "Remixed from: {$remix['remixed_from_video_id']}\n";
    
    // Step 4: Wait for remix to complete
    do {
        sleep(10);
        $remixStatus = OpenAIVideo::getStatus($remix['id']);
        echo "Remix status: {$remixStatus['status']} - Progress: {$remixStatus['progress']}%\n";
    } while ($remixStatus['status'] === 'processing');
    
    // Step 5: Download the remixed video
    if ($remixStatus['status'] === 'completed') {
        $path = OpenAIVideo::download($remixStatus['url'], 'videos/remixed.mp4');
        echo "Downloaded to: {$path}\n";
    }
}

Error Handling

try {
    $remix = OpenAIVideo::remix('video_123', 'New style prompt');
} catch (\Arbet\OpenAIVideo\Exceptions\OpenAIVideoException $e) {
    // Handle errors (e.g., video not found, video not completed, API error)
    echo "Error: {$e->getMessage()}\n";
}

Common Error Cases

  • Video not found: The provided video_id doesn't exist
  • Video not completed: Attempting to remix a video that's still processing
  • Invalid prompt: The prompt doesn't meet OpenAI's requirements

upload(string $filePath, string $purpose = 'assistants'): array

Upload a video or image file to OpenAI.

// Upload a video file
$upload = OpenAIVideo::upload('/path/to/video.mp4', 'assistants');

// Upload an image file for video generation
$imageUpload = OpenAIVideo::upload('/path/to/image.jpg', 'vision');

analyze(string $videoUrl, string $prompt, array $options = []): array

Analyze video content with AI.

$analysis = OpenAIVideo::analyze(
    'https://example.com/video.mp4',
    'What objects are visible in this video?',
    ['max_tokens' => 500]
);

getStatus(string $generationId): array

Get the status of a video generation request.

$status = OpenAIVideo::getStatus('gen_abc123');

download(string $url, string $savePath): string

Download a generated video to your storage.

$path = OpenAIVideo::download(
    'https://api.openai.com/v1/videos/abc123',
    'videos/my-video.mp4'
);

Error Handling

The package throws OpenAIVideoException for all API-related errors:

use Arbet\OpenAIVideo\Facades\OpenAIVideo;
use Arbet\OpenAIVideo\Exceptions\OpenAIVideoException;

try {
    $result = OpenAIVideo::generate('My video prompt');
} catch (OpenAIVideoException $e) {
    Log::error('OpenAI Video Error: ' . $e->getMessage());
    // Handle the error
}

Configuration Options

The config/openai-video.php file contains the following options:

  • api_key: Your OpenAI API key (from .env)
  • base_url: The OpenAI API base URL (default: https://api.openai.com/v1)
  • timeout: Request timeout in seconds (default: 120)
  • default_model: Default model to use for video operations (default: gpt-4o)

Testing

This package includes a comprehensive test suite to ensure reliability and quality before each release.

Running Tests

Run the full test suite:

composer test

Run tests with coverage report:

composer test-coverage

After running with coverage, open coverage/index.html in your browser to view the detailed coverage report.

Running Specific Test Groups

Run only unit tests:

vendor/bin/phpunit tests/Unit

Run only feature tests:

vendor/bin/phpunit tests/Feature

Run integration tests (requires real API key):

vendor/bin/phpunit --group integration

Test Structure

The test suite is organized into three categories:

  • Unit Tests (tests/Unit/): Test individual methods and classes in isolation using mocked dependencies
  • Feature Tests (tests/Feature/): Test Laravel integration, service provider, and facade functionality
  • Integration Tests (tests/Integration/): Optional tests that make real API calls (skipped by default)

Running Tests Before Release

Follow these steps before each release:

  1. Run the full test suite: composer test
  2. Check code coverage: composer test-coverage
  3. Verify all tests pass: Ensure no failures or warnings
  4. Review coverage report: Aim for 80%+ code coverage
  5. Run integration tests (optional): If you have API credentials

Continuous Integration

This package uses GitHub Actions for automated testing. Tests run automatically on:

  • Every push to main or develop branches
  • Every pull request to main or develop branches
  • Multiple PHP versions (8.1, 8.2, 8.3)
  • Multiple Laravel versions (10.x, 11.x)

Writing New Tests

When adding new features, always include corresponding tests:

  1. Add unit tests for new methods in tests/Unit/
  2. Add feature tests for Laravel integration in tests/Feature/
  3. Use mocked HTTP responses to avoid real API calls in unit tests
  4. Follow the existing test naming convention: it_can_do_something or it_throws_exception_when_...

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Security

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

Credits

License

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

Support

For support, please open an issue on GitHub or contact arbet@example.com.

Related Packages

Disclaimer

This package is not officially affiliated with OpenAI. Make sure to review OpenAI's terms of service and pricing before using their API.