Spaces:
Running
Running
| <script lang="ts"> | |
| import { generateSoraVideo } from '$lib/api/openai'; | |
| import type { SoraGenerationParams, SoraGenerationResult } from '$lib/types'; | |
| export let apiKey: string; | |
| export let params: SoraGenerationParams; | |
| export let onVideoGenerated: ((result: SoraGenerationResult) => void) | undefined = undefined; | |
| export let onProgress: ((progress: number) => void) | undefined = undefined; | |
| export let onError: ((error: string) => void) | undefined = undefined; | |
| let isGenerating = false; | |
| let progress = 0; | |
| let error: string | null = null; | |
| let statusText = ''; | |
| export async function generate() { | |
| if (isGenerating) return; | |
| isGenerating = true; | |
| progress = 0; | |
| error = null; | |
| statusText = 'Creating video generation job...'; | |
| try { | |
| const result = await generateSoraVideo( | |
| apiKey, | |
| params, | |
| (p) => { | |
| progress = p; | |
| statusText = `Generating video: ${Math.round(p)}%`; | |
| onProgress?.(p); | |
| } | |
| ); | |
| statusText = 'Video generated successfully!'; | |
| onVideoGenerated?.(result); | |
| } catch (err) { | |
| const errorMsg = err instanceof Error ? err.message : 'Unknown error'; | |
| error = errorMsg; | |
| statusText = 'Generation failed'; | |
| onError?.(errorMsg); | |
| } finally { | |
| isGenerating = false; | |
| } | |
| } | |
| </script> | |
| {#if isGenerating || error} | |
| <div class="generator-status"> | |
| {#if isGenerating} | |
| <div class="status generating"> | |
| <div class="spinner"></div> | |
| <p>{statusText}</p> | |
| {#if progress > 0} | |
| <div class="progress-bar"> | |
| <div class="progress-fill" style="width: {progress}%"></div> | |
| </div> | |
| {/if} | |
| </div> | |
| {/if} | |
| {#if error} | |
| <div class="status error"> | |
| <p>❌ {error}</p> | |
| </div> | |
| {/if} | |
| </div> | |
| {/if} | |
| <style> | |
| .generator-status { | |
| padding: 1.5rem; | |
| margin: 1rem 0; | |
| } | |
| .status { | |
| padding: 1.5rem; | |
| border-radius: 0.5rem; | |
| text-align: center; | |
| } | |
| .status.generating { | |
| background: #e3f2fd; | |
| border: 1px solid #bbdefb; | |
| color: #1565c0; | |
| } | |
| .status.error { | |
| background: #ffebee; | |
| border: 1px solid #ffcdd2; | |
| color: #c62828; | |
| } | |
| .status p { | |
| margin: 0; | |
| font-weight: 500; | |
| font-size: 1rem; | |
| } | |
| .spinner { | |
| width: 40px; | |
| height: 40px; | |
| margin: 0 auto 1rem; | |
| border: 4px solid #bbdefb; | |
| border-top-color: #1565c0; | |
| border-radius: 50%; | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| to { | |
| transform: rotate(360deg); | |
| } | |
| } | |
| .progress-bar { | |
| width: 100%; | |
| max-width: 400px; | |
| height: 8px; | |
| background: #bbdefb; | |
| border-radius: 4px; | |
| margin: 1rem auto 0; | |
| overflow: hidden; | |
| } | |
| .progress-fill { | |
| height: 100%; | |
| background: #1565c0; | |
| transition: width 0.3s ease; | |
| } | |
| </style> | |