File size: 15,357 Bytes
ee9377a | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | import React, { useState } from 'react';
import { Youtube, Download, Play, Pause, Volume2, FileAudio, FileVideo, Loader2, CheckCircle, AlertCircle, Globe, Mic, Languages, Speaker } from 'lucide-react';
interface ProcessingStep {
id: string;
title: string;
description: string;
status: 'pending' | 'processing' | 'completed' | 'error';
icon: React.ReactNode;
}
function App() {
const [youtubeUrl, setYoutubeUrl] = useState('');
const [isProcessing, setIsProcessing] = useState(false);
const [currentStep, setCurrentStep] = useState(0);
const [isPlaying, setIsPlaying] = useState(false);
const [showResults, setShowResults] = useState(false);
const processingSteps: ProcessingStep[] = [
{
id: 'extract',
title: 'Extract Audio',
description: 'Downloading video and extracting high-quality audio',
status: 'pending',
icon: <FileAudio className="w-5 h-5" />
},
{
id: 'transcribe',
title: 'Transcribe Speech',
description: 'Converting English audio to accurate text',
status: 'pending',
icon: <Mic className="w-5 h-5" />
},
{
id: 'translate',
title: 'Translate Content',
description: 'Translating English text to natural Hindi',
status: 'pending',
icon: <Languages className="w-5 h-5" />
},
{
id: 'synthesize',
title: 'Generate Audio',
description: 'Creating Hindi speech with natural voice',
status: 'pending',
icon: <Speaker className="w-5 h-5" />
},
{
id: 'sync',
title: 'Synchronize Video',
description: 'Matching audio timing with original video',
status: 'pending',
icon: <FileVideo className="w-5 h-5" />
}
];
const [steps, setSteps] = useState(processingSteps);
const isValidYouTubeUrl = (url: string) => {
const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.be)\/.+/;
return youtubeRegex.test(url);
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
if (!isValidYouTubeUrl(youtubeUrl)) return;
setIsProcessing(true);
setCurrentStep(0);
setShowResults(false);
// Simulate processing steps
for (let i = 0; i < steps.length; i++) {
setCurrentStep(i);
// Update step to processing
setSteps(prev => prev.map((step, index) =>
index === i ? { ...step, status: 'processing' } : step
));
// Simulate processing time
await new Promise(resolve => setTimeout(resolve, 2000 + Math.random() * 1000));
// Update step to completed
setSteps(prev => prev.map((step, index) =>
index === i ? { ...step, status: 'completed' } : step
));
}
setIsProcessing(false);
setShowResults(true);
};
const getStepStatusIcon = (step: ProcessingStep, index: number) => {
if (step.status === 'completed') {
return <CheckCircle className="w-5 h-5 text-emerald-500" />;
} else if (step.status === 'processing') {
return <Loader2 className="w-5 h-5 text-indigo-500 animate-spin" />;
} else if (step.status === 'error') {
return <AlertCircle className="w-5 h-5 text-red-500" />;
} else {
return <div className="w-5 h-5 rounded-full border-2 border-gray-300 flex items-center justify-center">
<div className="w-2 h-2 rounded-full bg-gray-300"></div>
</div>;
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-indigo-50 via-white to-purple-50">
{/* Header */}
<header className="bg-white/80 backdrop-blur-sm border-b border-gray-100 sticky top-0 z-50">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-4">
<div className="flex items-center space-x-3">
<div className="bg-gradient-to-r from-indigo-500 to-purple-600 p-2 rounded-xl">
<Globe className="w-6 h-6 text-white" />
</div>
<div>
<h1 className="text-xl font-bold text-gray-900">YouTube Translator</h1>
<p className="text-sm text-gray-600">English to Hindi Video Dubbing</p>
</div>
</div>
</div>
</header>
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
{/* Hero Section */}
<div className="text-center mb-12">
<div className="inline-flex items-center space-x-2 bg-indigo-100 text-indigo-700 px-4 py-2 rounded-full text-sm font-medium mb-6">
<Youtube className="w-4 h-4" />
<span>Powered by Advanced AI Translation</span>
</div>
<h2 className="text-4xl font-bold text-gray-900 mb-4">
Transform English Videos to
<span className="bg-gradient-to-r from-indigo-600 to-purple-600 bg-clip-text text-transparent"> Hindi</span>
</h2>
<p className="text-xl text-gray-600 max-w-2xl mx-auto">
Automatically extract, transcribe, translate, and dub YouTube videos with natural-sounding Hindi voiceovers
</p>
</div>
{/* Input Form */}
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 p-8 mb-8">
<form onSubmit={handleSubmit} className="space-y-6">
<div>
<label htmlFor="youtube-url" className="block text-sm font-semibold text-gray-700 mb-3">
YouTube Video URL
</label>
<div className="relative">
<Youtube className="absolute left-4 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
<input
type="url"
id="youtube-url"
value={youtubeUrl}
onChange={(e) => setYoutubeUrl(e.target.value)}
placeholder="https://www.youtube.com/watch?v=..."
className="w-full pl-12 pr-4 py-4 border border-gray-200 rounded-xl focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all duration-200 text-lg"
required
/>
</div>
{youtubeUrl && !isValidYouTubeUrl(youtubeUrl) && (
<p className="mt-2 text-sm text-red-600 flex items-center space-x-1">
<AlertCircle className="w-4 h-4" />
<span>Please enter a valid YouTube URL</span>
</p>
)}
</div>
<button
type="submit"
disabled={!isValidYouTubeUrl(youtubeUrl) || isProcessing}
className="w-full bg-gradient-to-r from-indigo-500 to-purple-600 text-white py-4 px-6 rounded-xl font-semibold text-lg hover:from-indigo-600 hover:to-purple-700 disabled:opacity-50 disabled:cursor-not-allowed transition-all duration-200 flex items-center justify-center space-x-2"
>
{isProcessing ? (
<>
<Loader2 className="w-5 h-5 animate-spin" />
<span>Processing...</span>
</>
) : (
<>
<Languages className="w-5 h-5" />
<span>Start Translation</span>
</>
)}
</button>
</form>
</div>
{/* Processing Steps */}
{(isProcessing || showResults) && (
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 p-8 mb-8">
<h3 className="text-2xl font-bold text-gray-900 mb-6">Processing Pipeline</h3>
<div className="space-y-4">
{steps.map((step, index) => (
<div key={step.id} className="flex items-center space-x-4 p-4 rounded-xl bg-gray-50 hover:bg-gray-100 transition-colors duration-200">
<div className="flex-shrink-0">
{getStepStatusIcon(step, index)}
</div>
<div className="flex-1">
<div className="flex items-center space-x-2">
{step.icon}
<h4 className="font-semibold text-gray-900">{step.title}</h4>
</div>
<p className="text-gray-600 text-sm mt-1">{step.description}</p>
</div>
{step.status === 'processing' && (
<div className="flex-shrink-0">
<div className="w-32 bg-gray-200 rounded-full h-2">
<div className="bg-indigo-500 h-2 rounded-full animate-pulse" style={{ width: '60%' }}></div>
</div>
</div>
)}
</div>
))}
</div>
</div>
)}
{/* Results Section */}
{showResults && (
<div className="bg-white rounded-2xl shadow-xl border border-gray-100 p-8">
<div className="flex items-center space-x-3 mb-6">
<CheckCircle className="w-8 h-8 text-emerald-500" />
<div>
<h3 className="text-2xl font-bold text-gray-900">Translation Complete!</h3>
<p className="text-gray-600">Your Hindi dubbed video is ready</p>
</div>
</div>
{/* Audio Player */}
<div className="bg-gradient-to-r from-gray-50 to-gray-100 rounded-xl p-6 mb-6">
<div className="flex items-center justify-between mb-4">
<h4 className="font-semibold text-gray-900">Hindi Audio Preview</h4>
<div className="flex items-center space-x-2 text-sm text-gray-600">
<Volume2 className="w-4 h-4" />
<span>2:34 duration</span>
</div>
</div>
{/* Waveform Visualization */}
<div className="bg-white rounded-lg p-4 mb-4">
<div className="flex items-center justify-center space-x-1 h-16">
{Array.from({ length: 50 }).map((_, i) => (
<div
key={i}
className="bg-indigo-400 rounded-full animate-pulse"
style={{
width: '3px',
height: `${Math.random() * 40 + 10}px`,
animationDelay: `${i * 0.1}s`
}}
></div>
))}
</div>
</div>
{/* Audio Controls */}
<div className="flex items-center justify-center space-x-4">
<button
onClick={() => setIsPlaying(!isPlaying)}
className="bg-indigo-500 hover:bg-indigo-600 text-white p-3 rounded-full transition-colors duration-200"
>
{isPlaying ? <Pause className="w-6 h-6" /> : <Play className="w-6 h-6" />}
</button>
<div className="flex-1 bg-gray-200 rounded-full h-2 max-w-md">
<div className="bg-indigo-500 h-2 rounded-full" style={{ width: '30%' }}></div>
</div>
<span className="text-sm text-gray-600">0:45 / 2:34</span>
</div>
</div>
{/* Download Options */}
<div className="grid md:grid-cols-2 gap-4">
<button className="flex items-center justify-center space-x-3 bg-emerald-50 hover:bg-emerald-100 text-emerald-700 border border-emerald-200 p-4 rounded-xl transition-colors duration-200">
<Download className="w-5 h-5" />
<div className="text-left">
<div className="font-semibold">Download Hindi Audio</div>
<div className="text-sm opacity-75">MP3 • 3.2 MB</div>
</div>
</button>
<button className="flex items-center justify-center space-x-3 bg-purple-50 hover:bg-purple-100 text-purple-700 border border-purple-200 p-4 rounded-xl transition-colors duration-200">
<Download className="w-5 h-5" />
<div className="text-left">
<div className="font-semibold">Download Dubbed Video</div>
<div className="text-sm opacity-75">MP4 • 15.7 MB</div>
</div>
</button>
</div>
{/* Quality Metrics */}
<div className="mt-6 p-4 bg-gray-50 rounded-xl">
<h5 className="font-semibold text-gray-900 mb-3">Translation Quality</h5>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className="text-center">
<div className="text-2xl font-bold text-emerald-600">98%</div>
<div className="text-sm text-gray-600">Accuracy</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-indigo-600">Natural</div>
<div className="text-sm text-gray-600">Voice Quality</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-purple-600">+99%</div>
<div className="text-sm text-gray-600">Sync Match</div>
</div>
<div className="text-center">
<div className="text-2xl font-bold text-orange-600">2:34</div>
<div className="text-sm text-gray-600">Duration</div>
</div>
</div>
</div>
</div>
)}
{/* Feature Highlights */}
{!isProcessing && !showResults && (
<div className="grid md:grid-cols-3 gap-6 mt-12">
<div className="bg-white rounded-xl p-6 shadow-lg border border-gray-100 hover:shadow-xl transition-shadow duration-200">
<div className="bg-indigo-100 w-12 h-12 rounded-lg flex items-center justify-center mb-4">
<Mic className="w-6 h-6 text-indigo-600" />
</div>
<h3 className="font-semibold text-gray-900 mb-2">High-Quality Transcription</h3>
<p className="text-gray-600 text-sm">Advanced AI accurately converts speech to text with 99%+ accuracy</p>
</div>
<div className="bg-white rounded-xl p-6 shadow-lg border border-gray-100 hover:shadow-xl transition-shadow duration-200">
<div className="bg-emerald-100 w-12 h-12 rounded-lg flex items-center justify-center mb-4">
<Languages className="w-6 h-6 text-emerald-600" />
</div>
<h3 className="font-semibold text-gray-900 mb-2">Natural Translation</h3>
<p className="text-gray-600 text-sm">Context-aware translation that preserves meaning and cultural nuances</p>
</div>
<div className="bg-white rounded-xl p-6 shadow-lg border border-gray-100 hover:shadow-xl transition-shadow duration-200">
<div className="bg-purple-100 w-12 h-12 rounded-lg flex items-center justify-center mb-4">
<Speaker className="w-6 h-6 text-purple-600" />
</div>
<h3 className="font-semibold text-gray-900 mb-2">Realistic Voice Synthesis</h3>
<p className="text-gray-600 text-sm">Natural-sounding Hindi voices with proper intonation and emotion</p>
</div>
</div>
)}
</div>
</div>
);
}
export default App; |