darkwood/media-bundle
Symfony CLI tool that converts a YAML video script into per-scene assets (voice and video), saves generation state, and outputs a render manifest. Supports Replicate-based providers, benchmark mode, and clear output file locations.
Installation:
composer require darkwood/media-bundle
Ensure bin/console is executable (chmod +x bin/console).
First Use Case: Generate a video from the provided example YAML:
php bin/console app:video:generate examples/video.yaml
Verify output in var/output/ (default directory; configurable via .env).
Where to Look First:
docs/mvp-video.md for environment variables (e.g., REPLICATE_API_TOKEN).examples/video.yaml for scene/asset structure.var/output/ for generated assets (e.g., scenes/, voiceovers/).Benchmark Mode: Test locally without API calls:
REPLICATE_MODE=benchmark php bin/console app:video:generate examples/video.yaml
Laravel Artisan Wrapper: Create a Laravel command to invoke the tool and handle output:
// app/Console/Commands/GenerateVideo.php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\Exception\ProcessFailedException;
class GenerateVideo extends Command
{
protected $signature = 'video:generate {yaml : Path to YAML file}';
protected $description = 'Generate video assets via Darkwood Media Bundle';
public function handle()
{
$process = new Process(['php', 'bin/console', 'app:video:generate', $this->argument('yaml')]);
$process->run();
if (!$process->isSuccessful()) {
throw new ProcessFailedException($process);
}
// Parse manifest and save to DB
$manifest = json_decode(file_get_contents(storage_path('app/video_manifests/manifest.json')), true);
$this->call('video:store-manifest', ['manifest' => json_encode($manifest)]);
}
}
YAML Generation: Dynamically create YAML in Laravel and pass it to the tool:
// Generate YAML from a model (e.g., VideoTemplate)
$yaml = "scenes:\n";
$yaml .= " - title: Welcome\n video: path/to/video.mp4\n voice: Hello, {$user->name}!\n";
file_put_contents(storage_path("app/video_templates/{$id}.yaml"), $yaml);
$this->call('video:generate', ['yaml' => "app/video_templates/{$id}.yaml"]);
Asset Management:
Use Laravel’s Storage facade to handle generated assets:
// Store manifest in DB
$manifest = json_decode(file_get_contents($manifestPath), true);
Video::create([
'manifest' => $manifest,
'asset_path' => 'storage/app/public/videos/' . $manifest['id'],
]);
// Serve assets via Laravel
Storage::disk('public')->put('videos/' . $manifest['id'] . '/final.mp4', file_get_contents($manifest['output_path']));
Template → Asset Pipeline:
Laravel Model/Service → YAML Generation → Darkwood Tool → Asset Output → Laravel DB/Storage
Error Handling:
monolog:
$process->run(function ($type, $buffer) {
if ($type === Process::ERR) {
\Log::error('Video Generation Error: ' . $buffer);
}
});
Queue-Based Generation:
// app/Jobs/GenerateVideoJob.php
namespace App\Jobs;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
class GenerateVideoJob implements ShouldQueue
{
use Queueable;
public function handle()
{
$this->call('video:generate', ['yaml' => $this->yamlPath]);
}
}
Environment Variables:
Sync Laravel’s .env with the tool’s requirements:
# Laravel .env
REPLICATE_API_TOKEN=${REPLICATE_API_TOKEN}
MEDIA_BUNDLE_OUTPUT=storage/app/public/videos
Testing:
// tests/Feature/VideoGenerationTest.php
public function test_video_generation()
{
$this->artisan('video:generate', ['yaml' => 'tests/fixtures/video.yaml'])
->expectsOutput('Success!')
->assertExitCode(0);
}
REPLICATE_MODE=benchmark ./vendor/bin/phpunit
Extending YAML Schema: Add custom fields to YAML and extend the tool’s configuration:
# Custom YAML example
scenes:
- title: Custom Scene
video: custom_path.mp4
voice: Custom text
custom_field: value # Extend as needed
Filesystem Permissions:
storage/app/) and the tool’s output path are writable by the PHP process.chmod -R 755 storage/ and verify the www-data (or equivalent) user has access.Replicate API Limits:
use Illuminate\Support\Facades\Http;
Http::retry(3, 100)->post('https://api.replicate.com/...');
YAML Schema Mismatches:
use Symfony\Component\Yaml\Yaml;
try {
$data = Yaml::parseFile($yamlPath);
} catch (\Exception $e) {
\Log::error("Invalid YAML: " . $e->getMessage());
throw new \InvalidArgumentException("YAML validation failed");
}
Stateful Runs:
var/run/. Concurrent runs may overwrite state.MEDIA_BUNDLE_PERSIST_STATE=false php bin/console app:video:generate video.yaml
Manifest Parsing:
"require": {
"darkwood/media-bundle": "8.0.12"
}
Verbose Output: Enable debug mode for detailed logs:
php bin/console app:video:generate video.yaml --verbose
Dry Runs: Test without generating assets:
MEDIA_BUNDLE_DRY_RUN=true php bin/console app:video:generate video.yaml
Tool-Specific Logs:
Check var/log/ for tool-generated logs. Redirect to Laravel’s log:
$process->run(function ($type, $buffer) {
\Log::info($buffer);
});
Laravel Service Provider:
Register the tool’s commands in Laravel’s AppServiceProvider:
public function boot()
{
$this->commands([
\Darkwood\MediaBundle\Command\GenerateVideoCommand::class,
]);
}
Custom Presets:
Extend the tool’s presets (e.g., Seedance 2.0) by overriding its configuration:
# config/media_bundle.yaml
presets:
custom_preset:
voice_model: "custom-model"
resolution: "1080p"
Asset Cleanup: Automate cleanup of old assets using Laravel’s scheduled tasks:
// app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
$schedule->command('video:cleanup-assets')->daily();
}
API Key Rotation:
Securely rotate Replicate API keys in Laravel’s .env and restart the queue worker:
php artisan queue:restart
Benchmarking: Use the tool’s benchmark mode to test performance locally:
REPLICATE_MODE=benchmark php bin/console app:video:generate video.yaml --benchmark
How can I help you explore Laravel packages today?