Escapingmatrixtoday/transcriptvortex-pro
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8">5 <meta name="viewport" content="width=device-width, initial-scale=1.0">6 <title>TranscribeVibe Pro - Video Transcript Generator</title>7 <link rel="icon" type="image/x-icon" href="/static/favicon.ico">8 <script src="https://cdn.tailwindcss.com"></script>9 <script src="https://unpkg.com/feather-icons"></script>10 <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>11 <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>12 <script src="https://cdn.jsdelivr.net/npm/animejs@3.2.1/lib/anime.min.js"></script>13 <script>14 tailwind.config = {15 theme: {16 extend: {17 colors: {18 primary: '#6366f1',19 secondary: '#8b5cf6',20 dark: '#1e293b'21 }22 }23 }24 }25 </script>26 <style>27 .vanta-container {28 position: absolute;29 top: 0;30 left: 0;31 width: 100%;32 height: 100%;33 z-index: -1;34 }35 .blur-effect {36 backdrop-filter: blur(8px);37 background-color: rgba(255, 255, 255, 0.1);38 }39 .dark .blur-effect {40 background-color: rgba(30, 41, 59, 0.7);41 }42 </style>43</head>44<body class="bg-gray-50 dark:bg-dark min-h-screen font-sans">45 <div id="vanta-bg" class="vanta-container"></div>46 47 <div class="container mx-auto px-4 py-12">48 <!-- Header -->49 <header class="mb-12 text-center">50 <h1 class="text-4xl md:text-5xl font-bold text-primary dark:text-white mb-4">51 <span class="inline-block transform hover:scale-105 transition-transform">TranscribeVibe Pro</span>52 </h1>53 <p class="text-xl text-gray-600 dark:text-gray-300 max-w-2xl mx-auto">54 Generate perfect transcripts from TikTok & YouTube videos instantly55 </p>56 </header>57 58 <!-- Main Card -->59 <div class="max-w-4xl mx-auto bg-white dark:bg-gray-800 rounded-xl shadow-2xl overflow-hidden transition-all duration-300 hover:shadow-primary/30">60 <!-- Tabs -->61 <div class="flex border-b border-gray-200 dark:border-gray-700">62 <button id="youtube-tab" class="flex-1 py-4 px-6 text-center font-medium text-gray-500 dark:text-gray-400 hover:text-primary dark:hover:text-primary border-b-2 border-transparent hover:border-primary transition-all active-tab">63 <i data-feather="youtube" class="inline mr-2"></i> YouTube64 </button>65 <button id="tiktok-tab" class="flex-1 py-4 px-6 text-center font-medium text-gray-500 dark:text-gray-400 hover:text-primary dark:hover:text-primary border-b-2 border-transparent hover:border-primary transition-all">66 <i data-feather="video" class="inline mr-2"></i> TikTok67 </button>68 </div>69 70 <!-- Content Area -->71 <div class="p-6 md:p-8">72 <!-- URL Input -->73 <div class="mb-8">74 <label for="video-url" class="block text-lg font-medium text-gray-700 dark:text-gray-300 mb-3">75 Paste your video URL here76 </label>77 <div class="flex flex-col md:flex-row gap-3">78 <input 79 type="text" 80 id="video-url" 81 placeholder="https://www.youtube.com/watch?v=..." 82 class="flex-1 px-5 py-3 border border-gray-300 dark:border-gray-600 rounded-lg focus:ring-2 focus:ring-primary focus:border-primary outline-none transition-all dark:bg-gray-700 dark:text-white"83 >84 <button id="generate-btn" class="px-6 py-3 bg-primary hover:bg-primary-600 text-white font-medium rounded-lg transition-all flex items-center justify-center gap-2">85 <i data-feather="play" class="w-5 h-5"></i> Generate Transcript86 </button>87 </div>88 </div>89 90 <!-- Status Indicator -->91 <div id="status-container" class="hidden mb-8">92 <div class="flex items-center gap-3">93 <div id="status-spinner" class="animate-spin">94 <i data-feather="loader" class="w-6 h-6 text-primary"></i>95 </div>96 <p id="status-text" class="text-gray-700 dark:text-gray-300">Processing your video...</p>97 </div>98 </div>99 <!-- Results -->100 <div id="results-container" class="hidden">101 <div class="flex flex-col md:flex-row gap-6 mb-6">102 <div class="flex-1">103 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-4">Full Transcript</h3>104 <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6 h-96 overflow-y-auto">105 <div id="transcript-content" class="prose dark:prose-invert max-w-none text-gray-700 dark:text-gray-300">106 <!-- Transcript will be inserted here -->107 </div>108 </div>109 </div>110 <div class="md:w-80">111 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-4">Video Info</h3>112 <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">113 <div id="video-info" class="space-y-4">114 <div>115 <p class="text-sm text-gray-500 dark:text-gray-400">Title:</p>116 <p id="video-title" class="font-medium">-</p>117 </div>118 <div>119 <p class="text-sm text-gray-500 dark:text-gray-400">Duration:</p>120 <p id="video-duration" class="font-medium">-</p>121 </div>122 <div>123 <p class="text-sm text-gray-500 dark:text-gray-400">Word Count:</p>124 <p id="word-count" class="font-medium">-</p>125 </div>126 <div class="pt-4 border-t border-gray-200 dark:border-gray-600">127 <p class="text-sm text-gray-500 dark:text-gray-400">Processing Time:</p>128 <p id="processing-time" class="font-medium">-</p>129 </div>130 </div>131 </div>132 </div>133 </div>134<div class="flex justify-between items-center mb-4">135 <h3 class="text-xl font-semibold text-gray-800 dark:text-white">Transcript</h3>136 <div class="flex gap-2">137 <button id="copy-btn" class="px-4 py-2 bg-gray-200 hover:bg-gray-300 dark:bg-gray-700 dark:hover:bg-gray-600 text-gray-800 dark:text-white rounded-lg transition-all flex items-center gap-2">138 <i data-feather="copy" class="w-4 h-4"></i> Copy139 </button>140 <button id="download-btn" class="px-4 py-2 bg-secondary hover:bg-secondary-600 text-white rounded-lg transition-all flex items-center gap-2">141 <i data-feather="download" class="w-4 h-4"></i> Download142 </button>143 </div>144 </div>145 <div class="bg-gray-50 dark:bg-gray-700 rounded-lg p-6">146 <div id="transcript-content" class="prose dark:prose-invert max-w-none text-gray-700 dark:text-gray-300">147 <!-- Transcript will be inserted here -->148 </div>149 </div>150 </div>151 152 <!-- Empty State -->153 <div id="empty-state" class="text-center py-12">154 <div class="inline-block p-5 bg-gray-100 dark:bg-gray-700 rounded-full mb-5">155 <i data-feather="file-text" class="w-10 h-10 text-gray-400 dark:text-gray-300"></i>156 </div>157 <h3 class="text-xl font-medium text-gray-600 dark:text-gray-300 mb-2">No Transcript Yet</h3>158 <p class="text-gray-500 dark:text-gray-400">Paste a YouTube or TikTok URL above to generate a transcript</p>159 </div>160 </div>161 </div>162 <!-- How It Works Section -->163 <div class="max-w-4xl mx-auto mt-20 mb-16">164 <h2 class="text-3xl font-bold text-center text-primary dark:text-white mb-12">How It Works</h2>165 <div class="space-y-8">166 <div class="flex flex-col md:flex-row gap-6 items-center">167 <div class="w-16 h-16 bg-primary-100 dark:bg-primary-900 rounded-full flex items-center justify-center shrink-0">168 <span class="text-2xl font-bold text-primary">1</span>169 </div>170 <div>171 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">Paste Video URL</h3>172 <p class="text-gray-600 dark:text-gray-400">Copy any YouTube or TikTok video URL and paste it into the input field above.</p>173 </div>174 </div>175 <div class="flex flex-col md:flex-row gap-6 items-center">176 <div class="w-16 h-16 bg-primary-100 dark:bg-primary-900 rounded-full flex items-center justify-center shrink-0">177 <span class="text-2xl font-bold text-primary">2</span>178 </div>179 <div>180 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">AI Processing</h3>181 <p class="text-gray-600 dark:text-gray-400">Our advanced AI analyzes the video's audio to extract every spoken word with timestamps.</p>182 </div>183 </div>184 <div class="flex flex-col md:flex-row gap-6 items-center">185 <div class="w-16 h-16 bg-primary-100 dark:bg-primary-900 rounded-full flex items-center justify-center shrink-0">186 <span class="text-2xl font-bold text-primary">3</span>187 </div>188 <div>189 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">Get Full Transcript</h3>190 <p class="text-gray-600 dark:text-gray-400">Receive a complete, word-for-word transcript with precise timestamps for every utterance.</p>191 </div>192 </div>193 </div>194 </div>195 196 <!-- Features Section -->197<div class="max-w-6xl mx-auto mt-16 grid grid-cols-1 md:grid-cols-3 gap-8">198 <div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg hover:shadow-primary/20 transition-all">199 <div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-4">200 <i data-feather="zap" class="w-6 h-6 text-primary"></i>201 </div>202 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">Lightning Fast</h3>203 <p class="text-gray-600 dark:text-gray-400">Get transcripts in seconds with our powerful processing engine.</p>204 </div>205 <div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg hover:shadow-primary/20 transition-all">206 <div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-4">207 <i data-feather="check-circle" class="w-6 h-6 text-primary"></i>208 </div>209 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">Highly Accurate</h3>210 <p class="text-gray-600 dark:text-gray-400">State-of-the-art speech recognition for perfect transcripts.</p>211 </div>212 <div class="bg-white dark:bg-gray-800 p-6 rounded-xl shadow-lg hover:shadow-primary/20 transition-all">213 <div class="w-12 h-12 bg-primary-100 dark:bg-primary-900 rounded-lg flex items-center justify-center mb-4">214 <i data-feather="lock" class="w-6 h-6 text-primary"></i>215 </div>216 <h3 class="text-xl font-semibold text-gray-800 dark:text-white mb-2">Private & Secure</h3>217 <p class="text-gray-600 dark:text-gray-400">Your data is processed securely and never stored.</p>218 </div>219 </div>220 221 <!-- Footer -->222 <footer class="mt-20 text-center text-gray-500 dark:text-gray-400">223 <p>© 2023 TranscribeVibe Pro. All rights reserved.</p>224 </footer>225 </div>226 227 <script>228 // Initialize Vanta.js background with error handling229 try {230 VANTA.GLOBE({231 el: "#vanta-bg",232 mouseControls: true,233 touchControls: true,234 gyroControls: false,235 minHeight: 200.00,236 minWidth: 200.00,237 scale: 1.00,238 scaleMobile: 1.00,239 color: 0x6366f1,240 backgroundColor: 0xffffff,241 size: 0.8242 });243 } catch (e) {244 console.error('Vanta.js initialization failed:', e);245 document.getElementById('vanta-bg').style.backgroundColor = '#6366f1';246 }247// Initialize feather icons248 feather.replace();249 // Tab switching functionality250 const youtubeTab = document.getElementById('youtube-tab');251 const tiktokTab = document.getElementById('tiktok-tab');252 253 function switchToYoutube() {254 youtubeTab.classList.add('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');255 tiktokTab.classList.remove('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');256 document.getElementById('video-url').placeholder = 'https://www.youtube.com/watch?v=...';257 }258 259 function switchToTiktok() {260 tiktokTab.classList.add('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');261 youtubeTab.classList.remove('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');262 document.getElementById('video-url').placeholder = 'https://www.tiktok.com/@username/video/...';263 }264 265 youtubeTab.addEventListener('click', switchToYoutube);266 tiktokTab.addEventListener('click', switchToTiktok);267 268 // Set initial active tab269 switchToYoutube();270// Transcript generation271 document.getElementById('generate-btn').addEventListener('click', async (e) => {272 e.preventDefault();273 const url = document.getElementById('video-url').value.trim();274 const urlPatterns = {275 youtube: /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+/,276 tiktok: /^(https?:\/\/)?(www\.)?tiktok\.com\/.+\/video\/.+/277 };278 279 if (!url) {280 alert('Please enter a valid YouTube or TikTok URL');281 return;282 }283 284 if (!urlPatterns.youtube.test(url) && !urlPatterns.tiktok.test(url)) {285 alert('Please enter a valid YouTube or TikTok URL');286 return;287 }288 289 // More detailed validation290 try {291 new URL(url);292 } catch (e) {293 alert('Invalid URL format. Please check and try again.');294 return;295 }296try {297 // Show loading state298 document.getElementById('empty-state').classList.add('hidden');299 document.getElementById('status-container').classList.remove('hidden');300 document.getElementById('results-container').classList.add('hidden');301 document.getElementById('generate-btn').disabled = true;302 document.getElementById('generate-btn').innerHTML = '<i data-feather="loader" class="animate-spin w-5 h-5"></i> Processing...';303 feather.replace();304 // Simulate API call with timeout305 const timeoutPromise = new Promise((_, reject) => 306 setTimeout(() => reject(new Error('Request timeout')), 10000)307 );308 309 // Show loading immediately310 document.getElementById('status-text').textContent = 'Connecting to API...';311 312 const apiPromise = Promise.all([313 fetchVideoInfo(url),314 fetchTranscript(url)315 ]);316 317 const [videoInfo, transcriptData] = await Promise.race([apiPromise, timeoutPromise]);318 console.log('API response received:', {videoInfo, transcriptData});319// Update UI320 document.getElementById('status-container').classList.add('hidden');321 document.getElementById('results-container').classList.remove('hidden');322 // Update video info section323 const videoInfoSection = document.getElementById('video-info');324 videoInfoSection.innerHTML = `325 <div>326 <p class="text-sm text-gray-500 dark:text-gray-400">Title:</p>327 <p id="video-title" class="font-medium">${videoInfo.title || 'Unknown'}</p>328 </div>329 <div>330 <p class="text-sm text-gray-500 dark:text-gray-400">Duration:</p>331 <p id="video-duration" class="font-medium">${videoInfo.duration || 'Unknown'}</p>332 </div>333 <div>334 <p class="text-sm text-gray-500 dark:text-gray-400">Word Count:</p>335 <p id="word-count" class="font-medium">${countWords(formatTranscript(transcriptData))}</p>336 </div>337 <div class="pt-4 border-t border-gray-200 dark:border-gray-600">338 <p class="text-sm text-gray-500 dark:text-gray-400">Processing Time:</p>339 <p id="processing-time" class="font-medium">${videoInfo.processingTime || 'Unknown'}</p>340 </div>341 `;342 343 // Update transcript with better formatting344 const fullTranscript = formatTranscript(transcriptData);345 const transcriptContainer = document.getElementById('transcript-content');346 transcriptContainer.innerHTML = fullTranscript;347 348 // Add copy handler to new content349 document.getElementById('copy-btn').onclick = null;350 document.getElementById('copy-btn').addEventListener('click', copyTranscript);351} catch (error) {352 console.error('Error:', error);353 document.getElementById('status-container').classList.add('hidden');354 document.getElementById('results-container').classList.add('hidden');355 document.getElementById('empty-state').classList.remove('hidden');356 357 const errorMessage = error.message.includes('timeout') 358 ? 'The request timed out. Please check your connection and try again.'359 : 'Error processing video. Please try again.';360 361 alert(errorMessage);362 } finally {363 document.getElementById('generate-btn').disabled = false;364 document.getElementById('generate-btn').innerHTML = '<i data-feather="play" class="w-5 h-5"></i> Generate Transcript';365 feather.replace();366 }367});368 // Helper functions369 async function fetchVideoInfo(url) {370 // Mock implementation with better error handling371 return new Promise((resolve, reject) => {372 setTimeout(() => {373 try {374 // Simulate occasional failure375 if (Math.random() < 0.1) {376 throw new Error('Random API failure simulation');377 }378 379 resolve({380 title: 'Sample Video Title',381 duration: '5:30',382 processingTime: `${(Math.random() * 2 + 1).toFixed(2)} seconds`383 });384 } catch (error) {385 console.error('fetchVideoInfo error:', error);386 reject(error);387 }388 }, 500);389 });390 }391 392 async function fetchTranscript(url) {393 // Mock implementation with better error handling394 return new Promise((resolve, reject) => {395 setTimeout(() => {396 try {397 // Simulate occasional failure398 if (Math.random() < 0.1) {399 throw new Error('Random API failure simulation');400 }401 402 const mockTranscript = [];403 for (let i = 0; i < 10; i++) {404 mockTranscript.push({405 timestamp: `00:${i.toString().padStart(2, '0')}`,406 text: `This is sample transcript line ${i + 1} for demonstration purposes.`407 });408 }409 resolve(mockTranscript);410 } catch (error) {411 console.error('fetchTranscript error:', error);412 reject(error);413 }414 }, 1000);415 });416 }417function formatTranscript(data) {418 return data.map(entry => 419 `<p><strong>[${entry.timestamp}]</strong> ${entry.text}</p>`420 ).join('');421 }422 423 function countWords(text) {424 return text.split(/\s+/).filter(word => word.length > 0).length;425 }426 // Copy functionality with fallback427 document.getElementById('copy-btn').addEventListener('click', async () => {428 const transcriptText = document.getElementById('transcript-content').textContent;429 const copyBtn = document.getElementById('copy-btn');430 431 try {432 if (navigator.clipboard) {433 await navigator.clipboard.writeText(transcriptText);434 } else {435 // Fallback for older browsers436 const textArea = document.createElement('textarea');437 textArea.value = transcriptText;438 document.body.appendChild(textArea);439 textArea.select();440 document.execCommand('copy');441 document.body.removeChild(textArea);442 }443 444 const originalText = copyBtn.innerHTML;445 copyBtn.innerHTML = '<i data-feather="check" class="w-4 h-4"></i> Copied!';446 copyBtn.classList.add('bg-green-500', 'hover:bg-green-600');447 feather.replace();448 449 setTimeout(() => {450 copyBtn.innerHTML = originalText;451 copyBtn.classList.remove('bg-green-500', 'hover:bg-green-600');452 feather.replace();453 }, 2000);454 } catch (err) {455 console.error('Failed to copy:', err);456 alert('Failed to copy transcript. Please try again or copy manually.');457 }458 });459// Download functionality460 document.getElementById('download-btn').addEventListener('click', () => {461 const transcriptText = document.getElementById('transcript-content').textContent;462 const blob = new Blob([transcriptText], { type: 'text/plain' });463 const url = URL.createObjectURL(blob);464 const a = document.createElement('a');465 a.href = url;466 a.download = 'transcript.txt';467 document.body.appendChild(a);468 a.click();469 document.body.removeChild(a);470 URL.revokeObjectURL(url);471 });472 // Dark mode detection and toggle with local storage473 function initDarkMode() {474 const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;475 const storedMode = localStorage.getItem('darkMode');476 477 if (storedMode === 'dark' || (storedMode === null && prefersDark)) {478 document.documentElement.classList.add('dark');479 }480 481 const darkModeToggle = document.createElement('button');482 darkModeToggle.className = 'fixed top-4 right-4 bg-gray-200 dark:bg-gray-700 p-2 rounded-full z-50 transition-all';483 darkModeToggle.setAttribute('aria-label', 'Toggle dark mode');484 darkModeToggle.innerHTML = document.documentElement.classList.contains('dark') 485 ? '<i data-feather="sun" class="w-5 h-5"></i>' 486 : '<i data-feather="moon" class="w-5 h-5"></i>';487 document.body.insertBefore(darkModeToggle, document.body.firstChild);488 489 darkModeToggle.addEventListener('click', () => {490 const isDark = document.documentElement.classList.toggle('dark');491 localStorage.setItem('darkMode', isDark ? 'dark' : 'light');492 493 const icon = darkModeToggle.querySelector('i');494 icon.setAttribute('data-feather', isDark ? 'sun' : 'moon');495 feather.replace();496 });497 }498 499 // Initialize dark mode after DOM is loaded500 document.addEventListener('DOMContentLoaded', initDarkMode);501 // Global error handling502 window.addEventListener('error', function(e) {503 console.error('Error:', e.message, 'in', e.filename, 'line', e.lineno);504 505 // Hide any loading states506 document.getElementById('status-container')?.classList.add('hidden');507 document.getElementById('generate-btn').disabled = false;508 document.getElementById('generate-btn').innerHTML = '<i data-feather="play" class="w-5 h-5"></i> Generate Transcript';509 510 // Show error message511 const errorDiv = document.createElement('div');512 errorDiv.className = 'fixed top-4 left-1/2 transform -translate-x-1/2 bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded z-50 flex items-center gap-2';513 errorDiv.innerHTML = `514 <i data-feather="alert-triangle" class="w-5 h-5"></i>515 <span>Something went wrong. Please try again.</span>516 `;517 document.body.appendChild(errorDiv);518 feather.replace();519 520 setTimeout(() => {521 errorDiv.classList.add('opacity-0', 'transition-opacity', 'duration-300');522 setTimeout(() => errorDiv.remove(), 300);523 }, 5000);524 });525 // Initialize with loading states and button handlers526 document.addEventListener('DOMContentLoaded', () => {527 feather.replace();528 document.getElementById('empty-state').classList.remove('hidden');529 530 // Ensure button handlers are properly attached531 document.getElementById('generate-btn').onclick = null;532 document.getElementById('generate-btn').addEventListener('click', generateTranscript);533 534 document.getElementById('copy-btn').onclick = null;535 document.getElementById('copy-btn').addEventListener('click', copyTranscript);536 537 document.getElementById('download-btn').onclick = null;538 document.getElementById('download-btn').addEventListener('click', downloadTranscript);539 });540 541 // Named functions for better debugging542 async function generateTranscript() {543 const url = document.getElementById('video-url').value.trim();544 // ... rest of the generate functionality ...545 }546 547 async function copyTranscript() {548 // ... copy functionality ...549 }550 551 function downloadTranscript() {552 // ... download functionality ...553 }554feather.replace();555 </script>556</body>557</html>558 