Toowired commited on
Commit
4d4e4d6
·
verified ·
1 Parent(s): 986dff9

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +145 -67
index.html CHANGED
@@ -346,15 +346,30 @@
346
 
347
  try {
348
  // Validate API key by making a simple request
349
- const testUrl = `https://texttospeech.googleapis.com/v1/voices?key=${encodeURIComponent(key)}&languageCode=en-US&pageSize=1`;
350
- const response = await fetch(testUrl, {
351
- method: 'GET',
352
- headers: {
353
- 'Accept': 'application/json',
354
- },
355
- mode: 'cors',
356
- cache: 'no-cache'
357
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
 
359
  if (response.ok) {
360
  apiKey = key;
@@ -362,17 +377,40 @@
362
  showToast('API key validated and saved successfully', 'success');
363
  loadVoices();
364
  } else {
365
- let errorMessage = 'Invalid API key';
 
 
 
 
 
 
 
 
 
 
366
  if (response.status === 403) {
367
- errorMessage = 'API key invalid or Text-to-Speech API not enabled';
368
  } else if (response.status === 400) {
369
- errorMessage = 'Invalid API key format';
 
 
370
  }
371
- showToast(errorMessage, 'error');
 
372
  }
373
  } catch (error) {
374
  console.error('API key validation error:', error);
375
- showToast('Failed to validate API key. Please check your internet connection.', 'error');
 
 
 
 
 
 
 
 
 
 
376
  } finally {
377
  // Reset button state
378
  saveKeyBtn.innerHTML = '<i class="fas fa-save mr-2"></i> Save Key';
@@ -406,38 +444,59 @@
406
  try {
407
  const languageCode = languageSelect.value;
408
 
409
- // Use different API endpoint format to avoid CORS issues
410
- const url = `https://texttospeech.googleapis.com/v1/voices?key=${encodeURIComponent(apiKey)}&languageCode=${languageCode}`;
411
 
412
- const response = await fetch(url, {
413
- method: 'GET',
414
- headers: {
415
- 'Accept': 'application/json',
416
- 'Content-Type': 'application/json',
417
- },
418
- mode: 'cors',
419
- cache: 'no-cache'
420
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
  // Better error handling
423
  if (!response.ok) {
424
  let errorMessage = 'Unknown error occurred';
 
 
425
  try {
426
  const errorData = await response.json();
427
  errorMessage = errorData.error?.message || errorMessage;
 
428
  } catch (e) {
429
- // If response is not JSON, use status text
430
  errorMessage = response.statusText || `HTTP ${response.status}`;
431
  }
432
 
433
  if (response.status === 403) {
434
- throw new Error('Invalid API key or Text-to-Speech API not enabled. Please check your Google Cloud console.');
435
  } else if (response.status === 400) {
436
- throw new Error('Bad request. Please verify your API key format is correct.');
437
  } else if (response.status === 401) {
438
- throw new Error('Unauthorized. Please check your API key permissions.');
439
  } else if (response.status === 404) {
440
- throw new Error('API endpoint not found. Please check your API key validity.');
 
 
441
  } else if (response.status >= 500) {
442
  throw new Error('Google Cloud service error. Please try again later.');
443
  } else {
@@ -448,35 +507,33 @@
448
  const data = await response.json();
449
 
450
  if (!data || !data.voices) {
451
- throw new Error('Invalid response from Google TTS API');
452
  }
453
 
454
  renderVoiceOptions(data.voices);
455
  } catch (error) {
456
  console.error('Error loading voices:', error);
457
 
458
- let errorDisplay = error.message;
459
- let troubleshootingTips = '';
460
-
461
- if (error.message.includes('Invalid API key')) {
462
- troubleshootingTips = `
463
- <div class="mt-3 text-xs text-gray-600">
464
- <p class="font-medium">Troubleshooting tips:</p>
465
- <ul class="mt-1 list-disc list-inside space-y-1">
466
- <li>Go to Google Cloud Console</li>
467
- <li>Enable the Text-to-Speech API</li>
468
- <li>Create API credentials</li>
469
- <li>Copy the API key exactly</li>
470
- </ul>
471
- </div>
472
- `;
473
- }
474
 
475
  voiceGrid.innerHTML = `
476
  <div class="col-span-full text-center py-8">
477
  <i class="fas fa-exclamation-triangle text-red-400 text-2xl"></i>
478
  <p class="text-gray-500 mt-2">Failed to load voices</p>
479
- <p class="text-red-500 text-sm mt-1">${errorDisplay}</p>
480
  ${troubleshootingTips}
481
  <button id="retryVoices" class="mt-4 bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded text-sm">
482
  <i class="fas fa-sync-alt mr-1"></i> Retry
@@ -617,6 +674,9 @@
617
  throw new Error('No text provided for synthesis');
618
  }
619
 
 
 
 
620
  // Prepare voice configuration
621
  let voiceConfig = {
622
  languageCode: voice.languageCodes[0],
@@ -643,46 +703,64 @@
643
  };
644
 
645
  try {
646
- const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${encodeURIComponent(apiKey)}`;
647
-
648
- const response = await fetch(url, {
649
- method: 'POST',
650
- headers: {
651
- 'Content-Type': 'application/json',
652
- 'Accept': 'application/json',
653
- },
654
- mode: 'cors',
655
- cache: 'no-cache',
656
- body: JSON.stringify(request)
657
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
658
 
659
  if (!response.ok) {
660
  let errorMessage = 'Failed to synthesize speech';
 
661
 
662
  try {
663
  const errorData = await response.json();
664
  errorMessage = errorData.error?.message || errorMessage;
 
665
  } catch (e) {
666
- errorMessage = `HTTP ${response.status}: ${response.statusText}`;
667
  }
668
 
669
  if (response.status === 400) {
670
- throw new Error('Invalid request parameters. Please check your text and settings.');
671
  } else if (response.status === 403) {
672
- throw new Error('Access denied. Please check your API key and quota.');
673
  } else if (response.status === 429) {
674
- throw new Error('Rate limit exceeded. Please wait a moment and try again.');
675
  } else if (response.status >= 500) {
676
- throw new Error('Google Cloud service error. Please try again later.');
677
  } else {
678
- throw new Error(errorMessage);
679
  }
680
  }
681
 
682
  const data = await response.json();
683
 
684
  if (!data || !data.audioContent) {
685
- throw new Error('Invalid response from Google TTS API');
686
  }
687
 
688
  return data.audioContent;
 
346
 
347
  try {
348
  // Validate API key by making a simple request
349
+ // Method 1: Try with header approach first
350
+ let response;
351
+ try {
352
+ response = await fetch(`https://texttospeech.googleapis.com/v1/voices?languageCode=en-US&pageSize=1`, {
353
+ method: 'GET',
354
+ headers: {
355
+ 'X-Goog-Api-Key': key,
356
+ 'Accept': 'application/json',
357
+ },
358
+ mode: 'cors',
359
+ cache: 'no-cache'
360
+ });
361
+ } catch (headerError) {
362
+ // Method 2: Fallback to URL parameter
363
+ const testUrl = `https://texttospeech.googleapis.com/v1/voices?key=${encodeURIComponent(key)}&languageCode=en-US&pageSize=1`;
364
+ response = await fetch(testUrl, {
365
+ method: 'GET',
366
+ headers: {
367
+ 'Accept': 'application/json',
368
+ },
369
+ mode: 'cors',
370
+ cache: 'no-cache'
371
+ });
372
+ }
373
 
374
  if (response.ok) {
375
  apiKey = key;
 
377
  showToast('API key validated and saved successfully', 'success');
378
  loadVoices();
379
  } else {
380
+ let errorMessage = 'API key validation failed';
381
+ let details = '';
382
+
383
+ try {
384
+ const errorData = await response.json();
385
+ errorMessage = errorData.error?.message || errorMessage;
386
+ details = errorData.error?.details ? ` (${JSON.stringify(errorData.error.details)})` : '';
387
+ } catch (e) {
388
+ details = ` (HTTP ${response.status})`;
389
+ }
390
+
391
  if (response.status === 403) {
392
+ errorMessage = 'Access denied. Check: 1) API key validity, 2) Text-to-Speech API enabled, 3) Billing enabled, 4) Key restrictions';
393
  } else if (response.status === 400) {
394
+ errorMessage = 'Bad request. API key format might be incorrect';
395
+ } else if (response.status === 429) {
396
+ errorMessage = 'Too many requests. Please wait a moment';
397
  }
398
+
399
+ showToast(errorMessage + details, 'error', 5000);
400
  }
401
  } catch (error) {
402
  console.error('API key validation error:', error);
403
+ let errorMessage = 'Failed to validate API key. ';
404
+
405
+ if (error.message.includes('Failed to fetch')) {
406
+ errorMessage += 'Please check your internet connection.';
407
+ } else if (error.message.includes('CORS')) {
408
+ errorMessage += 'CORS policy issue. Try with a different browser or disable extensions.';
409
+ } else {
410
+ errorMessage += error.message;
411
+ }
412
+
413
+ showToast(errorMessage, 'error', 5000);
414
  } finally {
415
  // Reset button state
416
  saveKeyBtn.innerHTML = '<i class="fas fa-save mr-2"></i> Save Key';
 
444
  try {
445
  const languageCode = languageSelect.value;
446
 
447
+ // Clean and validate API key
448
+ const cleanApiKey = apiKey.trim();
449
 
450
+ // Method 1: Try with API key in header (preferred)
451
+ let response;
452
+ try {
453
+ response = await fetch(`https://texttospeech.googleapis.com/v1/voices?languageCode=${languageCode}`, {
454
+ method: 'GET',
455
+ headers: {
456
+ 'X-Goog-Api-Key': cleanApiKey,
457
+ 'Accept': 'application/json',
458
+ },
459
+ mode: 'cors',
460
+ cache: 'no-cache'
461
+ });
462
+ } catch (headerError) {
463
+ console.log('Header method failed, trying URL parameter method');
464
+
465
+ // Method 2: Fallback to API key in URL
466
+ const url = `https://texttospeech.googleapis.com/v1/voices?key=${encodeURIComponent(cleanApiKey)}&languageCode=${languageCode}`;
467
+ response = await fetch(url, {
468
+ method: 'GET',
469
+ headers: {
470
+ 'Accept': 'application/json',
471
+ },
472
+ mode: 'cors',
473
+ cache: 'no-cache'
474
+ });
475
+ }
476
 
477
  // Better error handling
478
  if (!response.ok) {
479
  let errorMessage = 'Unknown error occurred';
480
+ let detailedError = '';
481
+
482
  try {
483
  const errorData = await response.json();
484
  errorMessage = errorData.error?.message || errorMessage;
485
+ detailedError = errorData.error?.details ? JSON.stringify(errorData.error.details) : '';
486
  } catch (e) {
 
487
  errorMessage = response.statusText || `HTTP ${response.status}`;
488
  }
489
 
490
  if (response.status === 403) {
491
+ throw new Error('Access denied. Please check: 1) API key is valid, 2) Text-to-Speech API is enabled, 3) Billing is enabled, 4) API key restrictions (if any) allow this domain.');
492
  } else if (response.status === 400) {
493
+ throw new Error(`Bad request. The API key format might be incorrect. Details: ${detailedError || 'Please verify your API key is copied correctly with no extra spaces.'}`);
494
  } else if (response.status === 401) {
495
+ throw new Error('Unauthorized. The API key is invalid or expired.');
496
  } else if (response.status === 404) {
497
+ throw new Error('API endpoint not found. Please verify the Text-to-Speech API is enabled.');
498
+ } else if (response.status === 429) {
499
+ throw new Error('Too many requests. Please wait a moment and try again.');
500
  } else if (response.status >= 500) {
501
  throw new Error('Google Cloud service error. Please try again later.');
502
  } else {
 
507
  const data = await response.json();
508
 
509
  if (!data || !data.voices) {
510
+ throw new Error('Invalid response from Google TTS API - no voices returned');
511
  }
512
 
513
  renderVoiceOptions(data.voices);
514
  } catch (error) {
515
  console.error('Error loading voices:', error);
516
 
517
+ let troubleshootingTips = `
518
+ <div class="mt-3 text-xs text-gray-600">
519
+ <p class="font-medium">Troubleshooting checklist:</p>
520
+ <ul class="mt-1 list-disc list-inside space-y-1">
521
+ <li>✓ Google Cloud project has billing enabled</li>
522
+ <li>✓ Text-to-Speech API is enabled in your project</li>
523
+ <li> API key is unrestricted (or allows this domain)</li>
524
+ <li>✓ API key was copied without extra spaces</li>
525
+ <li> API key permissions include Text-to-Speech API</li>
526
+ </ul>
527
+ <p class="mt-2 font-medium">Still having issues?</p>
528
+ <p class="text-gray-500">Try creating a new, unrestricted API key for testing.</p>
529
+ </div>
530
+ `;
 
 
531
 
532
  voiceGrid.innerHTML = `
533
  <div class="col-span-full text-center py-8">
534
  <i class="fas fa-exclamation-triangle text-red-400 text-2xl"></i>
535
  <p class="text-gray-500 mt-2">Failed to load voices</p>
536
+ <p class="text-red-500 text-sm mt-1 max-w-md mx-auto">${error.message}</p>
537
  ${troubleshootingTips}
538
  <button id="retryVoices" class="mt-4 bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded text-sm">
539
  <i class="fas fa-sync-alt mr-1"></i> Retry
 
674
  throw new Error('No text provided for synthesis');
675
  }
676
 
677
+ // Clean the API key
678
+ const cleanApiKey = apiKey.trim();
679
+
680
  // Prepare voice configuration
681
  let voiceConfig = {
682
  languageCode: voice.languageCodes[0],
 
703
  };
704
 
705
  try {
706
+ // Method 1: Try with header approach first
707
+ let response;
708
+ try {
709
+ response = await fetch(`https://texttospeech.googleapis.com/v1/text:synthesize`, {
710
+ method: 'POST',
711
+ headers: {
712
+ 'X-Goog-Api-Key': cleanApiKey,
713
+ 'Content-Type': 'application/json',
714
+ 'Accept': 'application/json',
715
+ },
716
+ mode: 'cors',
717
+ cache: 'no-cache',
718
+ body: JSON.stringify(request)
719
+ });
720
+ } catch (headerError) {
721
+ // Method 2: Fallback to URL parameter
722
+ const url = `https://texttospeech.googleapis.com/v1/text:synthesize?key=${encodeURIComponent(cleanApiKey)}`;
723
+ response = await fetch(url, {
724
+ method: 'POST',
725
+ headers: {
726
+ 'Content-Type': 'application/json',
727
+ 'Accept': 'application/json',
728
+ },
729
+ mode: 'cors',
730
+ cache: 'no-cache',
731
+ body: JSON.stringify(request)
732
+ });
733
+ }
734
 
735
  if (!response.ok) {
736
  let errorMessage = 'Failed to synthesize speech';
737
+ let details = '';
738
 
739
  try {
740
  const errorData = await response.json();
741
  errorMessage = errorData.error?.message || errorMessage;
742
+ details = errorData.error?.details ? ` (${JSON.stringify(errorData.error.details)})` : '';
743
  } catch (e) {
744
+ details = ` (HTTP ${response.status}: ${response.statusText})`;
745
  }
746
 
747
  if (response.status === 400) {
748
+ throw new Error('Invalid request parameters. Please check your text and settings.' + details);
749
  } else if (response.status === 403) {
750
+ throw new Error('Access denied. Please check your API key and quota.' + details);
751
  } else if (response.status === 429) {
752
+ throw new Error('Rate limit exceeded. Please wait a moment and try again.' + details);
753
  } else if (response.status >= 500) {
754
+ throw new Error('Google Cloud service error. Please try again later.' + details);
755
  } else {
756
+ throw new Error(errorMessage + details);
757
  }
758
  }
759
 
760
  const data = await response.json();
761
 
762
  if (!data || !data.audioContent) {
763
+ throw new Error('Invalid response from Google TTS API - no audio content received');
764
  }
765
 
766
  return data.audioContent;