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
Requires
- php: ^8.2
- guzzlehttp/guzzle: ^7.0
- illuminate/support: ^12.0
Requires (Dev)
- guzzlehttp/psr7: ^2.0
- mockery/mockery: ^1.6
- orchestra/testbench: ^10.0
- phpunit/phpunit: ^11.5.3
README
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_iddoesn'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:
- Run the full test suite:
composer test - Check code coverage:
composer test-coverage - Verify all tests pass: Ensure no failures or warnings
- Review coverage report: Aim for 80%+ code coverage
- 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
mainordevelopbranches - Every pull request to
mainordevelopbranches - 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:
- Add unit tests for new methods in
tests/Unit/ - Add feature tests for Laravel integration in
tests/Feature/ - Use mocked HTTP responses to avoid real API calls in unit tests
- Follow the existing test naming convention:
it_can_do_somethingorit_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.