There's still malfunctions. Check the entire system, button and all functions and fix it successfully
Browse files- index.html +127 -62
index.html
CHANGED
|
@@ -225,40 +225,49 @@
|
|
| 225 |
</div>
|
| 226 |
|
| 227 |
<script>
|
| 228 |
-
// Initialize Vanta.js background
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
feather.replace();
|
| 245 |
-
|
| 246 |
// Tab switching functionality
|
| 247 |
const youtubeTab = document.getElementById('youtube-tab');
|
| 248 |
const tiktokTab = document.getElementById('tiktok-tab');
|
| 249 |
|
| 250 |
-
|
| 251 |
youtubeTab.classList.add('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');
|
| 252 |
tiktokTab.classList.remove('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');
|
| 253 |
document.getElementById('video-url').placeholder = 'https://www.youtube.com/watch?v=...';
|
| 254 |
-
}
|
| 255 |
|
| 256 |
-
|
| 257 |
tiktokTab.classList.add('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');
|
| 258 |
youtubeTab.classList.remove('active-tab', 'text-primary', 'dark:text-primary', 'border-primary');
|
| 259 |
document.getElementById('video-url').placeholder = 'https://www.tiktok.com/@username/video/...';
|
| 260 |
-
}
|
| 261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 262 |
document.getElementById('generate-btn').addEventListener('click', async () => {
|
| 263 |
const url = document.getElementById('video-url').value.trim();
|
| 264 |
const urlPatterns = {
|
|
@@ -275,20 +284,27 @@
|
|
| 275 |
alert('Please enter a valid YouTube or TikTok URL');
|
| 276 |
return;
|
| 277 |
}
|
| 278 |
-
|
| 279 |
try {
|
| 280 |
// Show loading state
|
| 281 |
document.getElementById('empty-state').classList.add('hidden');
|
| 282 |
document.getElementById('status-container').classList.remove('hidden');
|
| 283 |
document.getElementById('results-container').classList.add('hidden');
|
| 284 |
document.getElementById('generate-btn').disabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 285 |
|
| 286 |
-
|
| 287 |
-
const [videoInfo, transcriptData] = await Promise.all([
|
| 288 |
fetchVideoInfo(url),
|
| 289 |
fetchTranscript(url)
|
| 290 |
]);
|
| 291 |
|
|
|
|
|
|
|
| 292 |
// Update UI
|
| 293 |
document.getElementById('status-container').classList.add('hidden');
|
| 294 |
document.getElementById('results-container').classList.remove('hidden');
|
|
@@ -308,11 +324,18 @@
|
|
| 308 |
document.getElementById('status-container').classList.add('hidden');
|
| 309 |
document.getElementById('results-container').classList.add('hidden');
|
| 310 |
document.getElementById('empty-state').classList.remove('hidden');
|
| 311 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 312 |
} finally {
|
| 313 |
document.getElementById('generate-btn').disabled = false;
|
|
|
|
|
|
|
| 314 |
}
|
| 315 |
-
|
| 316 |
|
| 317 |
// Helper functions
|
| 318 |
async function fetchVideoInfo(url) {
|
|
@@ -353,23 +376,40 @@
|
|
| 353 |
function countWords(text) {
|
| 354 |
return text.split(/\s+/).filter(word => word.length > 0).length;
|
| 355 |
}
|
| 356 |
-
// Copy functionality
|
| 357 |
-
document.getElementById('copy-btn').addEventListener('click', () => {
|
| 358 |
const transcriptText = document.getElementById('transcript-content').textContent;
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
const originalText = copyBtn.innerHTML;
|
| 362 |
copyBtn.innerHTML = '<i data-feather="check" class="w-4 h-4"></i> Copied!';
|
|
|
|
| 363 |
feather.replace();
|
| 364 |
|
| 365 |
setTimeout(() => {
|
| 366 |
copyBtn.innerHTML = originalText;
|
|
|
|
| 367 |
feather.replace();
|
| 368 |
}, 2000);
|
| 369 |
-
})
|
|
|
|
|
|
|
|
|
|
| 370 |
});
|
| 371 |
-
|
| 372 |
-
// Download functionality
|
| 373 |
document.getElementById('download-btn').addEventListener('click', () => {
|
| 374 |
const transcriptText = document.getElementById('transcript-content').textContent;
|
| 375 |
const blob = new Blob([transcriptText], { type: 'text/plain' });
|
|
@@ -382,41 +422,66 @@
|
|
| 382 |
document.body.removeChild(a);
|
| 383 |
URL.revokeObjectURL(url);
|
| 384 |
});
|
| 385 |
-
// Dark mode detection and toggle
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
darkModeToggle.innerHTML = document.documentElement.classList.contains('dark')
|
| 393 |
-
? '<i data-feather="sun" class="w-5 h-5"></i>'
|
| 394 |
-
: '<i data-feather="moon" class="w-5 h-5"></i>';
|
| 395 |
-
document.body.insertBefore(darkModeToggle, document.body.firstChild);
|
| 396 |
-
darkModeToggle.addEventListener('click', () => {
|
| 397 |
-
document.documentElement.classList.toggle('dark');
|
| 398 |
-
const icon = darkModeToggle.querySelector('i');
|
| 399 |
-
if (document.documentElement.classList.contains('dark')) {
|
| 400 |
-
icon.setAttribute('data-feather', 'sun');
|
| 401 |
-
} else {
|
| 402 |
-
icon.setAttribute('data-feather', 'moon');
|
| 403 |
}
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
window.addEventListener('error', function(e) {
|
| 408 |
-
console.error('
|
| 409 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 410 |
const errorDiv = document.createElement('div');
|
| 411 |
-
errorDiv.className = 'bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded
|
| 412 |
errorDiv.innerHTML = `
|
| 413 |
-
<
|
| 414 |
-
<span
|
| 415 |
`;
|
| 416 |
-
document.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 417 |
});
|
| 418 |
|
| 419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
</script>
|
| 421 |
</body>
|
| 422 |
</html>
|
|
|
|
| 225 |
</div>
|
| 226 |
|
| 227 |
<script>
|
| 228 |
+
// Initialize Vanta.js background with error handling
|
| 229 |
+
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.8
|
| 242 |
+
});
|
| 243 |
+
} catch (e) {
|
| 244 |
+
console.error('Vanta.js initialization failed:', e);
|
| 245 |
+
document.getElementById('vanta-bg').style.backgroundColor = '#6366f1';
|
| 246 |
+
}
|
| 247 |
+
// Initialize feather icons
|
| 248 |
feather.replace();
|
|
|
|
| 249 |
// Tab switching functionality
|
| 250 |
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 tab
|
| 269 |
+
switchToYoutube();
|
| 270 |
+
// Transcript generation
|
| 271 |
document.getElementById('generate-btn').addEventListener('click', async () => {
|
| 272 |
const url = document.getElementById('video-url').value.trim();
|
| 273 |
const urlPatterns = {
|
|
|
|
| 284 |
alert('Please enter a valid YouTube or TikTok URL');
|
| 285 |
return;
|
| 286 |
}
|
|
|
|
| 287 |
try {
|
| 288 |
// Show loading state
|
| 289 |
document.getElementById('empty-state').classList.add('hidden');
|
| 290 |
document.getElementById('status-container').classList.remove('hidden');
|
| 291 |
document.getElementById('results-container').classList.add('hidden');
|
| 292 |
document.getElementById('generate-btn').disabled = true;
|
| 293 |
+
document.getElementById('generate-btn').innerHTML = '<i data-feather="loader" class="animate-spin w-5 h-5"></i> Processing...';
|
| 294 |
+
feather.replace();
|
| 295 |
+
|
| 296 |
+
// Simulate API call with timeout
|
| 297 |
+
const timeoutPromise = new Promise((_, reject) =>
|
| 298 |
+
setTimeout(() => reject(new Error('Request timeout')), 10000)
|
| 299 |
+
);
|
| 300 |
|
| 301 |
+
const apiPromise = Promise.all([
|
|
|
|
| 302 |
fetchVideoInfo(url),
|
| 303 |
fetchTranscript(url)
|
| 304 |
]);
|
| 305 |
|
| 306 |
+
const [videoInfo, transcriptData] = await Promise.race([apiPromise, timeoutPromise]);
|
| 307 |
+
|
| 308 |
// Update UI
|
| 309 |
document.getElementById('status-container').classList.add('hidden');
|
| 310 |
document.getElementById('results-container').classList.remove('hidden');
|
|
|
|
| 324 |
document.getElementById('status-container').classList.add('hidden');
|
| 325 |
document.getElementById('results-container').classList.add('hidden');
|
| 326 |
document.getElementById('empty-state').classList.remove('hidden');
|
| 327 |
+
|
| 328 |
+
const errorMessage = error.message.includes('timeout')
|
| 329 |
+
? 'The request timed out. Please check your connection and try again.'
|
| 330 |
+
: 'Error processing video. Please try again.';
|
| 331 |
+
|
| 332 |
+
alert(errorMessage);
|
| 333 |
} finally {
|
| 334 |
document.getElementById('generate-btn').disabled = false;
|
| 335 |
+
document.getElementById('generate-btn').innerHTML = '<i data-feather="play" class="w-5 h-5"></i> Generate Transcript';
|
| 336 |
+
feather.replace();
|
| 337 |
}
|
| 338 |
+
});
|
| 339 |
|
| 340 |
// Helper functions
|
| 341 |
async function fetchVideoInfo(url) {
|
|
|
|
| 376 |
function countWords(text) {
|
| 377 |
return text.split(/\s+/).filter(word => word.length > 0).length;
|
| 378 |
}
|
| 379 |
+
// Copy functionality with fallback
|
| 380 |
+
document.getElementById('copy-btn').addEventListener('click', async () => {
|
| 381 |
const transcriptText = document.getElementById('transcript-content').textContent;
|
| 382 |
+
const copyBtn = document.getElementById('copy-btn');
|
| 383 |
+
|
| 384 |
+
try {
|
| 385 |
+
if (navigator.clipboard) {
|
| 386 |
+
await navigator.clipboard.writeText(transcriptText);
|
| 387 |
+
} else {
|
| 388 |
+
// Fallback for older browsers
|
| 389 |
+
const textArea = document.createElement('textarea');
|
| 390 |
+
textArea.value = transcriptText;
|
| 391 |
+
document.body.appendChild(textArea);
|
| 392 |
+
textArea.select();
|
| 393 |
+
document.execCommand('copy');
|
| 394 |
+
document.body.removeChild(textArea);
|
| 395 |
+
}
|
| 396 |
+
|
| 397 |
const originalText = copyBtn.innerHTML;
|
| 398 |
copyBtn.innerHTML = '<i data-feather="check" class="w-4 h-4"></i> Copied!';
|
| 399 |
+
copyBtn.classList.add('bg-green-500', 'hover:bg-green-600');
|
| 400 |
feather.replace();
|
| 401 |
|
| 402 |
setTimeout(() => {
|
| 403 |
copyBtn.innerHTML = originalText;
|
| 404 |
+
copyBtn.classList.remove('bg-green-500', 'hover:bg-green-600');
|
| 405 |
feather.replace();
|
| 406 |
}, 2000);
|
| 407 |
+
} catch (err) {
|
| 408 |
+
console.error('Failed to copy:', err);
|
| 409 |
+
alert('Failed to copy transcript. Please try again or copy manually.');
|
| 410 |
+
}
|
| 411 |
});
|
| 412 |
+
// Download functionality
|
|
|
|
| 413 |
document.getElementById('download-btn').addEventListener('click', () => {
|
| 414 |
const transcriptText = document.getElementById('transcript-content').textContent;
|
| 415 |
const blob = new Blob([transcriptText], { type: 'text/plain' });
|
|
|
|
| 422 |
document.body.removeChild(a);
|
| 423 |
URL.revokeObjectURL(url);
|
| 424 |
});
|
| 425 |
+
// Dark mode detection and toggle with local storage
|
| 426 |
+
function initDarkMode() {
|
| 427 |
+
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
| 428 |
+
const storedMode = localStorage.getItem('darkMode');
|
| 429 |
+
|
| 430 |
+
if (storedMode === 'dark' || (storedMode === null && prefersDark)) {
|
| 431 |
+
document.documentElement.classList.add('dark');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 432 |
}
|
| 433 |
+
|
| 434 |
+
const darkModeToggle = document.createElement('button');
|
| 435 |
+
darkModeToggle.className = 'fixed top-4 right-4 bg-gray-200 dark:bg-gray-700 p-2 rounded-full z-50 transition-all';
|
| 436 |
+
darkModeToggle.setAttribute('aria-label', 'Toggle dark mode');
|
| 437 |
+
darkModeToggle.innerHTML = document.documentElement.classList.contains('dark')
|
| 438 |
+
? '<i data-feather="sun" class="w-5 h-5"></i>'
|
| 439 |
+
: '<i data-feather="moon" class="w-5 h-5"></i>';
|
| 440 |
+
document.body.insertBefore(darkModeToggle, document.body.firstChild);
|
| 441 |
+
|
| 442 |
+
darkModeToggle.addEventListener('click', () => {
|
| 443 |
+
const isDark = document.documentElement.classList.toggle('dark');
|
| 444 |
+
localStorage.setItem('darkMode', isDark ? 'dark' : 'light');
|
| 445 |
+
|
| 446 |
+
const icon = darkModeToggle.querySelector('i');
|
| 447 |
+
icon.setAttribute('data-feather', isDark ? 'sun' : 'moon');
|
| 448 |
+
feather.replace();
|
| 449 |
+
});
|
| 450 |
+
}
|
| 451 |
+
|
| 452 |
+
// Initialize dark mode after DOM is loaded
|
| 453 |
+
document.addEventListener('DOMContentLoaded', initDarkMode);
|
| 454 |
+
// Global error handling
|
| 455 |
window.addEventListener('error', function(e) {
|
| 456 |
+
console.error('Error:', e.message, 'in', e.filename, 'line', e.lineno);
|
| 457 |
+
|
| 458 |
+
// Hide any loading states
|
| 459 |
+
document.getElementById('status-container')?.classList.add('hidden');
|
| 460 |
+
document.getElementById('generate-btn').disabled = false;
|
| 461 |
+
document.getElementById('generate-btn').innerHTML = '<i data-feather="play" class="w-5 h-5"></i> Generate Transcript';
|
| 462 |
+
|
| 463 |
+
// Show error message
|
| 464 |
const errorDiv = document.createElement('div');
|
| 465 |
+
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';
|
| 466 |
errorDiv.innerHTML = `
|
| 467 |
+
<i data-feather="alert-triangle" class="w-5 h-5"></i>
|
| 468 |
+
<span>Something went wrong. Please try again.</span>
|
| 469 |
`;
|
| 470 |
+
document.body.appendChild(errorDiv);
|
| 471 |
+
feather.replace();
|
| 472 |
+
|
| 473 |
+
setTimeout(() => {
|
| 474 |
+
errorDiv.classList.add('opacity-0', 'transition-opacity', 'duration-300');
|
| 475 |
+
setTimeout(() => errorDiv.remove(), 300);
|
| 476 |
+
}, 5000);
|
| 477 |
});
|
| 478 |
|
| 479 |
+
// Initialize with loading states
|
| 480 |
+
document.addEventListener('DOMContentLoaded', () => {
|
| 481 |
+
feather.replace();
|
| 482 |
+
document.getElementById('empty-state').classList.remove('hidden');
|
| 483 |
+
});
|
| 484 |
+
feather.replace();
|
| 485 |
</script>
|
| 486 |
</body>
|
| 487 |
</html>
|