| <?php |
|
|
| namespace App\Services; |
|
|
| class HuggingFaceVideoService |
| { |
| public function __construct( |
| private HuggingFaceImageService $huggingFaceImage, |
| private MotionVideoService $motionVideo, |
| ) { |
| } |
|
|
| public function generate(?string $sourceImageUrl, string $prompt, array $options = []): array |
| { |
| $cleanPrompt = trim($prompt); |
| $usesGeneratedFrame = false; |
| $imageUrl = trim((string) $sourceImageUrl); |
|
|
| if ($imageUrl === '') { |
| $images = $this->huggingFaceImage->generate($cleanPrompt, $this->resolveAspect($options), 1); |
| $imageUrl = trim((string) ($images[0] ?? '')); |
| $usesGeneratedFrame = true; |
| } |
|
|
| $video = $this->motionVideo->createFromImageUrl($imageUrl, $cleanPrompt, $options); |
|
|
| return [ |
| 'url' => $video['url'] ?? null, |
| 'provider' => 'huggingface-motion', |
| 'aspect' => $video['aspect'] ?? ($options['aspect_ratio'] ?? '16:9'), |
| 'prompt' => $cleanPrompt, |
| 'duration_seconds' => $video['duration_seconds'] ?? 8, |
| 'source_image_url' => $imageUrl, |
| 'applied_settings' => array_merge((array) ($video['applied_settings'] ?? []), [ |
| 'video_provider' => 'huggingface', |
| ]), |
| 'notice' => $usesGeneratedFrame |
| ? 'Start frame dibuat lewat Hugging Face, lalu dianimasikan dengan motion renderer lokal.' |
| : 'Source frame dipakai langsung, lalu dirender ke video dengan pipeline Hugging Face/local motion.', |
| 'async' => false, |
| ]; |
| } |
|
|
| private function resolveAspect(array $options): string |
| { |
| return (($options['aspect_ratio'] ?? '16:9') === '9:16') ? 'portrait' : 'landscape'; |
| } |
| } |