ttsspeak

tts

此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/534400/1580094/ttsspeak.js

  1. ;(() => {
  2. let vices = [], utterance, vice, viceMap = {}, playStat = 0, icon;
  3. let selectVice = GM_getValue('ttsVice', '自动选择');
  4. let rate = GM_getValue('ttsrate', 1);
  5. const setIcon = (i) => {
  6. if (!icon) {
  7. return
  8. }
  9. const pp = icon.parentElement.querySelector('button.pp');
  10. pp && (pp.innerHTML = i);
  11. }
  12. speechSynthesis.addEventListener("voiceschanged", () => {
  13. if (vices.length < 1) {
  14. vices = speechSynthesis.getVoices();
  15. utterance = new SpeechSynthesisUtterance();
  16.  
  17. utterance.addEventListener('end', () => {
  18. playStat = 0;
  19. setIcon('▶️');
  20. })
  21. utterance.addEventListener('pause', (e) => {
  22. playStat = 2;
  23. setIcon('▶️');
  24. })
  25. utterance.addEventListener('resume', (e) => {
  26. playStat = 1;
  27. setIcon('⏸️');
  28. })
  29. vices.map(v => viceMap[v.voiceURI] = v);
  30. }
  31. });
  32.  
  33. function play(text, vice = null) {
  34. utterance.voice = vice ? vice : viceMap[selectVice];
  35. utterance.text = text;
  36. utterance.rate = rate;
  37. playStat = 1;
  38. speechSynthesis.speak(utterance);
  39. }
  40.  
  41. function speak(speakText) {
  42. if (viceMap[selectVice]) {
  43. play(speakText);
  44. return
  45. }
  46. const la = eld.detect(speakText).language;
  47. console.log(la);
  48. for (const value of vices) {
  49. const lang = value.lang.toLowerCase();
  50. if (lang.indexOf(la) > -1) {
  51. vice = value
  52. break;
  53. }
  54. }
  55. if (!vice) {
  56. icon.title = '似乎无可用的tts,请先安装';
  57. return
  58. }
  59. play(speakText, vice);
  60. }
  61.  
  62. PushIconAction({
  63. name: 'tts发音 右键设置语速和语言',
  64. id: 'icon-speech',
  65. image: GM_getResourceURL('icon-speak'),
  66. trigger: function (speakText, _, ev) {
  67. if (vices.length < 1) {
  68. ev.target.title = 'tts还没有准备好,请稍等';
  69. return
  70. }
  71. speak(speakText);
  72. },
  73. call: (img) => {
  74. img.addEventListener('contextmenu', (e) => {
  75. e.preventDefault();
  76. const content = img.parentElement.querySelector('tr-content');
  77. content.style.display = 'block';
  78. const arr = vices.map(v => [`${v.lang} - ${v.localService ? 'local' : ''}-${v.name}`, v.voiceURI]);
  79. arr.unshift(['自动选择', '']);
  80. const options = buildOption(arr, selectVice, 1, 0);
  81. content.querySelector('div').innerHTML = `
  82. <div class="item">
  83. <button class="pp">${playStat === 1 ? '⏸️' : '▶️'}</button>
  84. <button class="stop">⏹️</button>
  85.  
  86. </div>
  87. <div class="item">
  88. <label for="speakspeed">语速:</label>
  89. <input id="speakspeed" value="1" min="0" step="0.1" type="number">
  90. </div>
  91. <div class="item">
  92. <label for="language">语言:</label>
  93. <select id="language">${options}</select>
  94. </div>
  95. `;
  96. icon = content.querySelector('.pp');
  97. content.querySelector('.stop').addEventListener('click', () => {
  98. speechSynthesis.cancel();
  99. playStat = 0;
  100. icon.innerHTML = '▶️';
  101. });
  102. icon.addEventListener('click', function (e) {
  103. switch (playStat) {
  104. case 0:
  105. speak(window.getSelection().toString().trim());
  106. this.innerHTML = '⏸️';
  107. break;
  108. case 1:
  109. speechSynthesis.pause();
  110. this.innerHTML = '▶️';
  111. break;
  112. case 2:
  113. speechSynthesis.resume();
  114. this.innerHTML = '⏸️';
  115. break;
  116. default:
  117. break;
  118. }
  119.  
  120. })
  121. content.querySelector('#speakspeed').addEventListener('change', function (e) {
  122. rate = this.value;
  123. GM_setValue('ttsrate', rate);
  124. });
  125. content.querySelector('#language').addEventListener('change', function (e) {
  126. selectVice = this.value;
  127. GM_setValue('ttsVice', this.value);
  128. })
  129. })
  130. },
  131. hide: (icon) => {
  132. speechSynthesis.cancel();
  133. setIcon('▶️');
  134. playStat = 0;
  135. }
  136. })
  137. })();
  138.  
  139.  

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址