sonygod commited on
Commit
501e832
·
1 Parent(s): a068a69
Files changed (1) hide show
  1. youtube_sub.js +92 -18
youtube_sub.js CHANGED
@@ -520,6 +520,7 @@ GM_addStyle(`
520
  this.setupUI();
521
  this.setupEventListeners();
522
  window.subtitleManagerInstance = this;
 
523
  }
524
  cleanup() {
525
  // Remove existing instance
@@ -943,8 +944,8 @@ GM_addStyle(`
943
  popup = document.createElement('div');
944
  popup.className = 'word-definition-popup';
945
 
946
-
947
-
948
  // Create markdown container
949
  const markdownDiv = document.createElement('div');
950
  markdownDiv.className = 'markdown';
@@ -971,10 +972,24 @@ GM_addStyle(`
971
  speakerBtn.onclick = (e) => {
972
  e.stopPropagation();
973
  try {
974
- const utterance = new SpeechSynthesisUtterance(wordText); // Use current wordText
975
  utterance.lang = 'en-US';
976
  utterance.rate = 0.9;
 
 
 
 
 
 
 
 
 
 
 
 
 
977
  speechSynthesis.speak(utterance);
 
978
  } catch (error) {
979
  console.error('TTS failed:', error);
980
  }
@@ -993,24 +1008,52 @@ GM_addStyle(`
993
 
994
  try {
995
  // Use GM_xmlhttpRequest with updated config
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
996
  const response = await new Promise((resolve, reject) => {
997
  GM_xmlhttpRequest({
998
- method: 'POST',
999
- url: 'https://sonygod-flash.hf.space/ask',
1000
  headers: {
1001
- 'Content-Type': 'application/json',
1002
  'Accept': 'application/json',
1003
  'Origin': 'https://www.youtube.com'
1004
  },
1005
- data: JSON.stringify({
1006
- prompt: `中文翻译 "${word.text.trim()}"并详细介绍,100字以内,包括对应的英文近义词,英文反义词`,
1007
- model: 'GEMINI'
1008
- }),
1009
  onload: function (response) {
1010
- // Handle different status codes
1011
- if (response.status === 405) {
1012
- reject(new Error('API endpoint does not accept POST method. Try GET instead.'));
1013
- } else if (response.status >= 200 && response.status < 300) {
1014
  try {
1015
  resolve(JSON.parse(response.responseText));
1016
  } catch (e) {
@@ -1033,7 +1076,13 @@ GM_addStyle(`
1033
 
1034
  const content = response?.data?.response;
1035
  if (!content) {
1036
- throw new Error('No content in API response');
 
 
 
 
 
 
1037
  }
1038
 
1039
  // Create text node instead of using innerHTML
@@ -1073,13 +1122,38 @@ GM_addStyle(`
1073
 
1074
  } catch (error) {
1075
  console.error('API call failed:', error);
1076
- popup.querySelector('.markdown').innerHTML = `
 
1077
  <div style="color: red">
1078
  Error: ${error.message}<br>
1079
  Please check console for details.
1080
  </div>
1081
- `;
1082
- popup.classList.add('show');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1083
  }
1084
  };
1085
  textContainer.appendChild(wordBtn);
 
520
  this.setupUI();
521
  this.setupEventListeners();
522
  window.subtitleManagerInstance = this;
523
+ this.speakerClickCount = 0; // Add counter
524
  }
525
  cleanup() {
526
  // Remove existing instance
 
944
  popup = document.createElement('div');
945
  popup.className = 'word-definition-popup';
946
 
947
+
948
+
949
  // Create markdown container
950
  const markdownDiv = document.createElement('div');
951
  markdownDiv.className = 'markdown';
 
972
  speakerBtn.onclick = (e) => {
973
  e.stopPropagation();
974
  try {
975
+ const utterance = new SpeechSynthesisUtterance(wordText);
976
  utterance.lang = 'en-US';
977
  utterance.rate = 0.9;
978
+
979
+ // Get voices and select based on click count
980
+ const voices = speechSynthesis.getVoices();
981
+ const isFemaleTurn = this.speakerClickCount % 2 === 1;
982
+ const voice = voices.find(v =>
983
+ v.lang.includes('en') &&
984
+ v.name.toLowerCase().includes('female') === isFemaleTurn
985
+ );
986
+
987
+ if (voice) {
988
+ utterance.voice = voice;
989
+ }
990
+
991
  speechSynthesis.speak(utterance);
992
+ this.speakerClickCount++; // Increment counter
993
  } catch (error) {
994
  console.error('TTS failed:', error);
995
  }
 
1008
 
1009
  try {
1010
  // Use GM_xmlhttpRequest with updated config
1011
+ // const response = await new Promise((resolve, reject) => {
1012
+ // GM_xmlhttpRequest({
1013
+ // method: 'POST',
1014
+ // url: 'https://sonygod-flash.hf.space/ask',
1015
+ // headers: {
1016
+ // 'Content-Type': 'application/json',
1017
+ // 'Accept': 'application/json',
1018
+ // 'Origin': 'https://www.youtube.com'
1019
+ // },
1020
+ // data: JSON.stringify({
1021
+ // prompt: `中文翻译 "${word.text.trim()}"并详细介绍,100字以内,包括对应的英文近义词,英文反义词`,
1022
+ // model: 'GEMINI'
1023
+ // }),
1024
+ // onload: function (response) {
1025
+ // // Handle different status codes
1026
+ // if (response.status === 405) {
1027
+ // reject(new Error('API endpoint does not accept POST method. Try GET instead.'));
1028
+ // } else if (response.status >= 200 && response.status < 300) {
1029
+ // try {
1030
+ // resolve(JSON.parse(response.responseText));
1031
+ // } catch (e) {
1032
+ // reject(new Error('Invalid JSON response'));
1033
+ // }
1034
+ // } else {
1035
+ // reject(new Error(`HTTP error! status: ${response.status}`));
1036
+ // }
1037
+ // },
1038
+ // onerror: function (error) {
1039
+ // reject(new Error('Network request failed: ' + error.error));
1040
+ // },
1041
+ // ontimeout: function () {
1042
+ // reject(new Error('Request timed out'));
1043
+ // }
1044
+ // });
1045
+ // });
1046
+
1047
  const response = await new Promise((resolve, reject) => {
1048
  GM_xmlhttpRequest({
1049
+ method: 'GET',
1050
+ url: `https://sonygod-flash.hf.space/translate/${encodeURIComponent(word.text.trim())}`,
1051
  headers: {
 
1052
  'Accept': 'application/json',
1053
  'Origin': 'https://www.youtube.com'
1054
  },
 
 
 
 
1055
  onload: function (response) {
1056
+ if (response.status >= 200 && response.status < 300) {
 
 
 
1057
  try {
1058
  resolve(JSON.parse(response.responseText));
1059
  } catch (e) {
 
1076
 
1077
  const content = response?.data?.response;
1078
  if (!content) {
1079
+
1080
+ // Check for error response
1081
+ if (response?.status && response?.error) {
1082
+ throw new Error(`API Error: ${response.status} - ${response.error}`);
1083
+ } else {
1084
+ throw new Error('No content in API response');
1085
+ }
1086
  }
1087
 
1088
  // Create text node instead of using innerHTML
 
1122
 
1123
  } catch (error) {
1124
  console.error('API call failed:', error);
1125
+ // Create error message HTML
1126
+ const errorHtml = `
1127
  <div style="color: red">
1128
  Error: ${error.message}<br>
1129
  Please check console for details.
1130
  </div>
1131
+ `;
1132
+
1133
+ // Sanitize error message
1134
+ const sanitizedError = DOMPurify.sanitize(errorHtml, {
1135
+ RETURN_TRUSTED_TYPE: true
1136
+ });
1137
+
1138
+ // Create temporary container
1139
+ const errorContainer = document.createElement('div');
1140
+ errorContainer.className = 'markdown';
1141
+
1142
+ try {
1143
+ errorContainer.innerHTML = sanitizedError;
1144
+
1145
+ // Clear existing content
1146
+ const markdownElement = popup.querySelector('.markdown');
1147
+ while (markdownElement.firstChild) {
1148
+ markdownElement.removeChild(markdownElement.firstChild);
1149
+ }
1150
+
1151
+ // Append sanitized error message
1152
+ markdownElement.appendChild(errorContainer);
1153
+ popup.classList.add('show');
1154
+ } catch (e) {
1155
+ console.error('Error displaying message:', e);
1156
+ }
1157
  }
1158
  };
1159
  textContainer.appendChild(wordBtn);