| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>TranscriptFlow AI - Elite Edition</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <script src="https://unpkg.com/feather-icons"></script> |
| <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script> |
| <script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script> |
| <style> |
| .transcript-container { |
| scrollbar-width: thin; |
| scrollbar-color: #4F46E5 #E5E7EB; |
| } |
| .transcript-container::-webkit-scrollbar { |
| width: 8px; |
| } |
| .transcript-container::-webkit-scrollbar-track { |
| background: #E5E7EB; |
| } |
| .transcript-container::-webkit-scrollbar-thumb { |
| background-color: #4F46E5; |
| border-radius: 4px; |
| } |
| .waveform { |
| animation: wave 1.5s ease-in-out infinite; |
| } |
| @keyframes wave { |
| 0%, 100% { transform: translateY(0); } |
| 50% { transform: translateY(-8px); } |
| } |
| </style> |
| </head> |
| <body class="bg-gray-50 min-h-screen"> |
| <div id="vanta-bg" class="fixed inset-0 -z-10 opacity-20"></div> |
| |
| <div class="container mx-auto px-4 py-12"> |
| |
| <header class="text-center mb-12"> |
| <h1 class="text-4xl md:text-5xl font-bold text-gray-800 mb-3"> |
| TranscriptFlow <span class="text-indigo-600">AI</span> |
| </h1> |
| <p class="text-lg text-gray-600 max-w-2xl mx-auto"> |
| Elite transcription service with verbatim accuracy and word-level timestamps |
| </p> |
| </header> |
|
|
| |
| <div class="flex flex-col lg:flex-row gap-8"> |
| |
| <div class="w-full lg:w-1/3 bg-white rounded-xl shadow-lg p-6"> |
| <div class="mb-6"> |
| <label for="video-url" class="block text-sm font-medium text-gray-700 mb-2"> |
| YouTube or TikTok URL |
| </label> |
| <div class="flex gap-2"> |
| <input |
| type="text" |
| id="video-url" |
| placeholder="https://youtube.com/watch?v=..." |
| class="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500" |
| > |
| <button id="paste-btn" class="px-4 py-3 bg-gray-100 hover:bg-gray-200 rounded-lg transition"> |
| <i data-feather="clipboard"></i> |
| </button> |
| </div> |
| <p id="url-error" class="mt-1 text-sm text-red-600 hidden">Please enter a valid YouTube or TikTok URL</p> |
| </div> |
|
|
| <div class="mb-6"> |
| <label class="block text-sm font-medium text-gray-700 mb-2"> |
| Transcription Options |
| </label> |
| <div class="space-y-3"> |
| <div class="flex items-center"> |
| <input id="timestamps" type="checkbox" checked class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"> |
| <label for="timestamps" class="ml-2 block text-sm text-gray-700">Include timestamps</label> |
| </div> |
| <div class="flex items-center"> |
| <input id="filler-words" type="checkbox" checked class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"> |
| <label for="filler-words" class="ml-2 block text-sm text-gray-700">Include filler words</label> |
| </div> |
| </div> |
| </div> |
|
|
| <button id="transcribe-btn" class="w-full py-3 px-4 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg shadow-md transition flex items-center justify-center"> |
| <span id="btn-text">Generate Transcript</span> |
| <svg id="btn-spinner" class="animate-spin -mr-1 ml-2 h-5 w-5 text-white hidden" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"> |
| <circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle> |
| <path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path> |
| </svg> |
| </button> |
|
|
| <div id="progress-container" class="mt-6 hidden"> |
| <div class="flex justify-between text-sm text-gray-600 mb-1"> |
| <span id="progress-stage">Initializing</span> |
| <span id="progress-percent">0%</span> |
| </div> |
| <div class="w-full bg-gray-200 rounded-full h-2.5"> |
| <div id="progress-bar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="w-full lg:w-2/3 bg-white rounded-xl shadow-lg overflow-hidden"> |
| <div class="p-4 border-b border-gray-200 flex justify-between items-center bg-gray-50"> |
| <h2 class="text-lg font-medium text-gray-700">Transcript</h2> |
| <div class="flex gap-2" id="transcript-controls"> |
| <button class="p-2 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded transition" title="Increase font"> |
| <i data-feather="plus"></i> |
| </button> |
| <button class="p-2 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded transition" title="Decrease font"> |
| <i data-feather="minus"></i> |
| </button> |
| <button class="p-2 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded transition" title="Toggle word wrap"> |
| <i data-feather="type"></i> |
| </button> |
| <button class="p-2 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded transition" title="Copy to clipboard"> |
| <i data-feather="copy"></i> |
| </button> |
| <button class="p-2 text-gray-500 hover:text-indigo-600 hover:bg-gray-100 rounded transition" title="Download"> |
| <i data-feather="download"></i> |
| </button> |
| </div> |
| </div> |
| <div id="transcript-container" class="transcript-container p-6 max-h-[70vh] overflow-y-auto bg-white"> |
| <div id="transcript-placeholder" class="text-center py-12 text-gray-400"> |
| <div class="mx-auto w-16 h-16 mb-4 flex items-center justify-center"> |
| <div class="waveform inline-block w-2 h-8 mx-0.5 bg-indigo-200 rounded-sm"></div> |
| <div class="waveform inline-block w-2 h-12 mx-0.5 bg-indigo-300 rounded-sm delay-100"></div> |
| <div class="waveform inline-block w-2 h-16 mx-0.5 bg-indigo-400 rounded-sm delay-200"></div> |
| <div class="waveform inline-block w-2 h-12 mx-0.5 bg-indigo-300 rounded-sm delay-100"></div> |
| <div class="waveform inline-block w-2 h-8 mx-0.5 bg-indigo-200 rounded-sm"></div> |
| </div> |
| <p class="text-lg">Your transcript will appear here</p> |
| <p class="text-sm mt-1">Paste a YouTube or TikTok URL and click "Generate Transcript"</p> |
| </div> |
| <div id="transcript-content" class="hidden"></div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="toast-container" class="fixed bottom-4 right-4 space-y-2 w-80"></div> |
| </div> |
|
|
| <script> |
| feather.replace(); |
| |
| |
| VANTA.GLOBE({ |
| el: "#vanta-bg", |
| mouseControls: true, |
| touchControls: true, |
| gyroControls: false, |
| minHeight: 200.00, |
| minWidth: 200.00, |
| scale: 1.00, |
| scaleMobile: 1.00, |
| color: "#6366f1", |
| backgroundColor: "#f9fafb", |
| size: 0.8 |
| }); |
| |
| |
| const videoUrlInput = document.getElementById('video-url'); |
| const pasteBtn = document.getElementById('paste-btn'); |
| const transcribeBtn = document.getElementById('transcribe-btn'); |
| const btnText = document.getElementById('btn-text'); |
| const btnSpinner = document.getElementById('btn-spinner'); |
| const progressContainer = document.getElementById('progress-container'); |
| const progressBar = document.getElementById('progress-bar'); |
| const progressStage = document.getElementById('progress-stage'); |
| const progressPercent = document.getElementById('progress-percent'); |
| const transcriptPlaceholder = document.getElementById('transcript-placeholder'); |
| const transriptContent = document.getElementById('transcript-content'); |
| const urlError = document.getElementById('url-error'); |
| const toastContainer = document.getElementById('toast-container'); |
| |
| |
| const YOUTUBE_REGEX = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/; |
| const TIKTOK_REGEX = /^(https?:\/\/)?(www\.)?(tiktok\.com|vt\.tiktok\.com)\/.+$/; |
| |
| |
| let isProcessing = false; |
| |
| |
| pasteBtn.addEventListener('click', handlePaste); |
| transcribeBtn.addEventListener('click', handleTranscribe); |
| videoUrlInput.addEventListener('input', validateUrl); |
| |
| |
| function validateUrl() { |
| const url = videoUrlInput.value.trim(); |
| if (!url) { |
| urlError.classList.add('hidden'); |
| return true; |
| } |
| |
| const isValid = YOUTUBE_REGEX.test(url) || TIKTOK_REGEX.test(url); |
| if (isValid) { |
| urlError.classList.add('hidden'); |
| return true; |
| } else { |
| urlError.classList.remove('hidden'); |
| return false; |
| } |
| } |
| |
| function handlePaste() { |
| navigator.clipboard.readText() |
| .then(text => { |
| videoUrlInput.value = text; |
| validateUrl(); |
| }) |
| .catch(err => { |
| showToast('Clipboard access denied', 'error'); |
| }); |
| } |
| |
| function handleTranscribe() { |
| if (isProcessing) return; |
| |
| const url = videoUrlInput.value.trim(); |
| if (!url || !validateUrl()) { |
| showToast('Please enter a valid URL', 'error'); |
| return; |
| } |
| |
| |
| isProcessing = true; |
| btnText.textContent = 'Processing...'; |
| btnSpinner.classList.remove('hidden'); |
| transcribeBtn.disabled = true; |
| progressContainer.classList.remove('hidden'); |
| transriptContent.classList.add('hidden'); |
| transcriptPlaceholder.classList.remove('hidden'); |
| |
| |
| simulateTranscription(); |
| } |
| |
| function simulateTranscription() { |
| const stages = [ |
| { name: 'Fetching video', duration: 2000, percent: 20 }, |
| { name: 'Extracting audio', duration: 3000, percent: 40 }, |
| { name: 'Transcribing content', duration: 5000, percent: 80 }, |
| { name: 'Formatting results', duration: 2000, percent: 100 } |
| ]; |
| |
| let currentStage = 0; |
| |
| function processStage() { |
| if (currentStage >= stages.length) { |
| completeTranscription(); |
| return; |
| } |
| |
| const stage = stages[currentStage]; |
| progressStage.textContent = stage.name; |
| progressBar.style.width = `${stage.percent}%`; |
| progressPercent.textContent = `${stage.percent}%`; |
| |
| setTimeout(() => { |
| currentStage++; |
| processStage(); |
| }, stage.duration); |
| } |
| |
| processStage(); |
| } |
| |
| function completeTranscription() { |
| |
| isProcessing = false; |
| btnText.textContent = 'Generate Transcript'; |
| btnSpinner.classList.add('hidden'); |
| transcribeBtn.disabled = false; |
| |
| |
| transcriptPlaceholder.classList.add('hidden'); |
| transriptContent.classList.remove('hidden'); |
| |
| |
| transriptContent.innerHTML = ` |
| <div class="mb-2 text-sm text-gray-500">00:00:00</div> |
| <p class="mb-4">[Intro music] Welcome to this demonstration of TranscriptFlow AI, the elite transcription service that delivers verbatim accuracy with word-level timestamps.</p> |
| |
| <div class="mb-2 text-sm text-gray-500">00:00:12</div> |
| <p class="mb-4">Um, today we'll be showing you how to, uh, get the most accurate transcripts from YouTube and TikTok videos. The system preserves all filler words like "um" and "uh" for complete authenticity.</p> |
| |
| <div class="mb-2 text-sm text-gray-500">00:00:32</div> |
| <p class="mb-4">You can see the, uh, timestamps on the left here, which are word-aligned in the actual API response. This makes it perfect for, uh, video editing or detailed analysis.</p> |
| |
| <div class="mb-2 text-sm text-gray-500">00:00:48</div> |
| <p class="mb-4">Try it yourself by pasting any YouTube or TikTok URL above. The system handles videos of any length with enterprise-grade reliability.</p> |
| `; |
| |
| showToast('Transcript generated successfully!', 'success'); |
| } |
| |
| function showToast(message, type = 'info') { |
| const toast = document.createElement('div'); |
| toast.className = `p-4 rounded-lg shadow-md text-white ${type === 'error' ? 'bg-red-500' : type === 'success' ? 'bg-green-500' : 'bg-indigo-500'}`; |
| toast.innerHTML = ` |
| <div class="flex items-center"> |
| <i data-feather="${type === 'error' ? 'alert-circle' : type === 'success' ? 'check-circle' : 'info'}" class="mr-2"></i> |
| <span>${message}</span> |
| </div> |
| `; |
| toastContainer.appendChild(toast); |
| feather.replace(); |
| |
| setTimeout(() => { |
| toast.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
| setTimeout(() => toast.remove(), 300); |
| }, 5000); |
| } |
| </script> |
| </body> |
| </html> |
|
|