Spaces:
Sleeping
Sleeping
Download frontend/src/components/ProviderSelect.tsx from userIdc2024/Video_AdGenesis_App: direct link, hf CLI and curl.
- Browser
- Download file 9.65 kB
-
https://huggingface.co/spaces/userIdc2024/Video_AdGenesis_App/resolve/main/frontend/src/components/ProviderSelect.tsx
- Command line
-
hf download hf://spaces/userIdc2024/Video_AdGenesis_App/frontend/src/components/ProviderSelect.tsx
-
curl -L -o ProviderSelect.tsx https://huggingface.co/spaces/userIdc2024/Video_AdGenesis_App/resolve/main/frontend/src/components/ProviderSelect.tsx
9.65 kB
| import React from 'react'; | |
| import { motion } from 'framer-motion'; | |
| import { VideoProvider } from '@/types'; | |
| import { ZapIcon, BrainIcon, CheckIcon } from './Icons'; | |
| type PricingSamples = { '1s'?: number; '8s'?: number; '30s'?: number; '60s'?: number }; | |
| interface ProviderCard { | |
| id: VideoProvider; | |
| name: string; | |
| subtitle?: string; | |
| description?: string; | |
| icon: React.FC<any>; | |
| color: 'coral' | 'electric'; | |
| features: string[]; | |
| badge?: string; | |
| pricing?: { per_second?: number | null; samples?: PricingSamples; evidence?: string | null }; | |
| } | |
| interface ProviderSelectProps { | |
| onSelect: (provider: VideoProvider) => void; | |
| } | |
| const providers: ProviderCard[] = [ | |
| { | |
| id: 'kling' as VideoProvider, | |
| name: 'Kie API', | |
| subtitle: '(cheap use less credits)', | |
| description: 'High-quality video generation with advanced continuity. Perfect for professional UGC and talking head videos.', | |
| icon: ZapIcon, | |
| color: 'coral', | |
| features: [ | |
| 'Veo 3.1 Fast Model', | |
| 'Image-to-Video & Text-to-Video', | |
| 'Video Extension (Seamless)', | |
| 'GPT-4o Prompt Generation', | |
| '9:16 Portrait Format', | |
| 'Voice Type Selection', | |
| ], | |
| badge: 'Recommended', | |
| pricing: { | |
| per_second: 0.05, | |
| samples: { | |
| '1s': 0.05, | |
| '8s': 0.4, | |
| '30s': 1.5, | |
| '60s': 3.0, | |
| }, | |
| evidence: 'Veo 3 Fast (8s, with audio) — Kie.ai $0.40 (≈$0.05/sec)', | |
| }, | |
| }, | |
| { | |
| id: 'replicate' as VideoProvider, | |
| name: 'Replicate API', | |
| subtitle: '(more credits, more expensive)', | |
| description: 'Access to diverse video generation models. Great for experimentation and unique creative styles.', | |
| icon: BrainIcon, | |
| color: 'electric', | |
| features: [ | |
| 'Multiple Model Options', | |
| 'Flexible Duration', | |
| 'Creative Styles', | |
| 'Cost Effective', | |
| 'API Simplicity', | |
| 'Community Models', | |
| ], | |
| badge: 'Flexible', | |
| pricing: { | |
| per_second: 0.15, | |
| samples: { | |
| '1s': 0.15, | |
| '8s': 1.2, | |
| '30s': 4.5, | |
| '60s': 9.0, | |
| }, | |
| evidence: 'google/veo-3-fast with_audio — $0.15 per second', | |
| }, | |
| }, | |
| ]; | |
| export const ProviderSelect: React.FC<ProviderSelectProps> = ({ onSelect }) => { | |
| return ( | |
| <div className="min-h-[70vh] flex flex-col items-center justify-center p-8"> | |
| {/* Hero Section */} | |
| <motion.div | |
| className="text-center mb-16" | |
| initial={{ opacity: 0, y: -20 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.6 }} | |
| > | |
| <h1 className="text-5xl md:text-6xl font-display font-bold mb-4"> | |
| <span className="gradient-text">Video AdGenesis</span> | |
| <span className="text-void-200"> Studio</span> | |
| </h1> | |
| <p className="text-xl text-void-400 max-w-2xl mx-auto"> | |
| Transform your scripts into stunning videos with AI. Choose your preferred generation engine to get started. | |
| </p> | |
| </motion.div> | |
| {/* Provider Cards */} | |
| <div className="grid grid-cols-1 md:grid-cols-2 gap-8 max-w-5xl w-full"> | |
| {providers.map((provider, index) => ( | |
| <motion.div | |
| key={provider.id} | |
| initial={{ opacity: 0, y: 30 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| transition={{ duration: 0.5, delay: index * 0.15 }} | |
| > | |
| <button | |
| onClick={() => onSelect(provider.id)} | |
| className={` | |
| w-full text-left provider-card card-interactive | |
| ${provider.color === 'coral' ? 'hover:border-coral-500/50 hover:glow-coral' : 'hover:border-electric-500/50 hover:glow-electric'} | |
| group transition-all duration-500 | |
| `} | |
| > | |
| {/* Badge */} | |
| <div className="flex items-start justify-between mb-6"> | |
| <div className={` | |
| w-16 h-16 rounded-2xl flex items-center justify-center | |
| ${provider.color === 'coral' | |
| ? 'bg-coral-500/10 text-coral-400 group-hover:bg-coral-500/20' | |
| : 'bg-electric-500/10 text-electric-400 group-hover:bg-electric-500/20' | |
| } | |
| transition-colors duration-300 | |
| `}> | |
| <provider.icon size={32} /> | |
| </div> | |
| <span className={` | |
| px-3 py-1 rounded-full text-xs font-semibold | |
| ${provider.color === 'coral' | |
| ? 'bg-coral-500/20 text-coral-300' | |
| : 'bg-electric-500/20 text-electric-300' | |
| } | |
| `}> | |
| {provider.badge} | |
| </span> | |
| </div> | |
| {/* Title & Description */} | |
| <h2 className="text-2xl font-display font-bold text-void-100 mb-1"> | |
| {provider.name} | |
| </h2> | |
| <p className={`text-sm mb-3 ${provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'}`}> | |
| {provider.subtitle} | |
| </p> | |
| <p className="text-void-400 text-sm mb-6 leading-relaxed"> | |
| {provider.description} | |
| </p> | |
| {/* Pricing (per sec, 8s, 30s, 1m) */} | |
| {provider.pricing?.per_second != null && ( | |
| <div className="mt-4 mb-4"> | |
| <div className={` | |
| rounded-xl p-4 | |
| ${provider.color === 'coral' | |
| ? 'bg-gradient-to-br from-coral-500/5 to-coral-600/10 border border-coral-500/20' | |
| : 'bg-gradient-to-br from-electric-500/5 to-electric-600/10 border border-electric-500/20' | |
| } | |
| `}> | |
| <div className="grid grid-cols-4 gap-3"> | |
| {(['1s', '8s', '30s', '60s'] as Array<keyof PricingSamples>).map((k) => { | |
| const v = provider.pricing?.samples?.[k as keyof typeof provider.pricing.samples]; | |
| return ( | |
| <div | |
| key={k} | |
| className={` | |
| flex flex-col items-center justify-center p-3 rounded-lg | |
| ${provider.color === 'coral' | |
| ? 'bg-void-900/40 hover:bg-void-800/60' | |
| : 'bg-void-900/40 hover:bg-void-800/60' | |
| } | |
| transition-all duration-300 group/price | |
| `} | |
| > | |
| <div className={` | |
| text-[10px] uppercase font-semibold tracking-wider mb-1 | |
| ${provider.color === 'coral' ? 'text-coral-400/70' : 'text-electric-400/70'} | |
| group-hover/price:opacity-100 transition-opacity | |
| `}> | |
| {k} | |
| </div> | |
| <div className={` | |
| text-xl md:text-2xl font-bold leading-none | |
| ${provider.color === 'coral' | |
| ? 'text-coral-300 group-hover/price:text-coral-200' | |
| : 'text-electric-300 group-hover/price:text-electric-200' | |
| } | |
| transition-colors duration-300 | |
| `}> | |
| {v != null ? `$${v.toFixed(2)}` : '—'} | |
| </div> | |
| </div> | |
| ); | |
| })} | |
| </div> | |
| <div className="mt-3 pt-3 border-t border-void-700/30 flex items-center justify-center"> | |
| <span className="text-[10px] text-void-500 uppercase tracking-wider"> | |
| Per second rate: ${provider.pricing.per_second?.toFixed(4) || '—'} | |
| </span> | |
| </div> | |
| </div> | |
| </div> | |
| )} | |
| {/* Features */} | |
| <div className="grid grid-cols-2 gap-2 mt-6"> | |
| {provider.features.map((feature, i) => ( | |
| <div key={i} className="flex items-center gap-2"> | |
| <CheckIcon | |
| size={14} | |
| className={provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'} | |
| /> | |
| <span className="text-xs text-void-300">{feature}</span> | |
| </div> | |
| ))} | |
| </div> | |
| {/* CTA Arrow */} | |
| <div className={` | |
| mt-6 flex items-center gap-2 text-sm font-semibold | |
| ${provider.color === 'coral' ? 'text-coral-400' : 'text-electric-400'} | |
| group-hover:gap-4 transition-all duration-300 | |
| `}> | |
| <span>Get Started</span> | |
| <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" /> | |
| </svg> | |
| </div> | |
| </button> | |
| </motion.div> | |
| ))} | |
| </div> | |
| {/* Footer Note */} | |
| <motion.p | |
| className="mt-12 text-void-500 text-sm text-center" | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| transition={{ delay: 0.8 }} | |
| > | |
| Both providers use your Python backend. API keys are configured server-side. | |
| </motion.p> | |
| </div> | |
| ); | |
| }; | |