-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.js
238 lines (220 loc) · 8.36 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
// ==UserScript==
// @name Youtube Untranslater
// @namespace https://github.com/rickymohk/YoutubeUntranslater/
// @version 0.1
// @description Remove auto-translated youtube titles. Inspired by pcouy/YoutubeAutotranslateCanceler, rewritten in mordern way.
// @author Ricky Mo
// @match https://www.youtube.com/
// @match https://youtube.com/
// @match https://www.youtube.com/watch*
// @match https://youtube.com/watch*
// @grant GM.setValue
// @grant GM.getValue
// @grant GM.deleteValue
// ==/UserScript==
const TAG = "YoutubeUntranslater"
const log = {
error(...data){
console.error(TAG,...data);
},
info(...data){
console.info(TAG,...data);
}
}
const API_KEY = "api_key";
async function getApiKey()
{
let apiKey = await GM.getValue(API_KEY);
if(!apiKey)
{
apiKey = prompt("Enter your API key. Go to https://developers.google.com/youtube/v3/getting-started to know how to obtain an API key, then go to https://console.developers.google.com/apis/api/youtube.googleapis.com/ in order to enable Youtube Data API for your key.");
await GM.setValue(API_KEY,apiKey);
}
return apiKey;
}
function getVideoId(node)
{
// log.info("anchorToVideoId a.href",a.href);
let a = node;
while(a.tagName != "A"){
a = a.parentNode;
}
try {
const url = new URL(a.href);
if(url.pathname == "/watch")
{
return new URLSearchParams(url.search).get("v");
}
else if(url.pathname.includes("/shorts/"))
{
return url.pathname.split("/")[2];
}
} catch (err) {
log.error(err);
log.info("Error anchor",a);
}
return undefined;
}
let currentLocation;
let isMainChanged = false;
let isPreviewChanged = false;
let cachedTitles = {};
let revertedAnchors = new Set();
let isApiKeyValid = false;
let noApiKey = true;
function reset()
{
log.info("Page change detected. reset");
currentLocation = document.title;
isMainChanged = false;
isPreviewChanged = false;
revertedAnchors.clear();
}
function linkify(inputText) {
var replacedText, replacePattern1, replacePattern2, replacePattern3;
//URLs starting with http://, https://, or ftp://
replacePattern1 = /(\b(https?|ftp):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gim;
replacedText = inputText.replace(replacePattern1, '<a class="yt-simple-endpoint style-scope yt-formatted-string" spellcheck="false" href="$1">$1</a>');
//URLs starting with "www." (without // before it, or it'd re-link the ones done above).
replacePattern2 = /(^|[^\/])(www\.[\S]+(\b|$))/gim;
replacedText = replacedText.replace(replacePattern2, '<a class="yt-simple-endpoint style-scope yt-formatted-string" spellcheck="false" href="http://$1">$1</a>');
//Change email addresses to mailto:: links.
replacePattern3 = /(([a-zA-Z0-9\-\_\.])+@[a-zA-Z\_]+?(\.[a-zA-Z]{2,6})+)/gim;
replacedText = replacedText.replace(replacePattern3, '<a class="yt-simple-endpoint style-scope yt-formatted-string" spellcheck="false" href="mailto:$1">$1</a>');
return replacedText;
}
async function untranslate(apiKey)
{
if(currentLocation != document.title)
{
reset();
}
if(noApiKey){
log.error("No API key");
return;
}
const isLive = document.querySelector(".ytp-live") != null;
let mainVidId;
// log.info("isMainChanged",isMainChanged);
if(!isMainChanged && !isLive)
{
mainVidId = window.location.pathname == "/watch" && new URLSearchParams(location.search).get("v");
}
// mainVidId = undefined;
const spans = [...document.querySelectorAll("span#video-title")].filter(a => !revertedAnchors.has(a));
const ytFormattedStrings = [...document.querySelectorAll("yt-formatted-string#video-title:not(.ytd-video-preview)")].filter(a => !revertedAnchors.has(a));
let preview = document.querySelector("#preview #details:not([hidden]) yt-formatted-string.ytd-video-preview");
if(!preview)
{
isPreviewChanged = false;
}
else if(isPreviewChanged)
{
preview = undefined;
}
let nodes = [...spans,...ytFormattedStrings,preview].filter(it => it);
// log.info("mainVidId",mainVidId);
// log.info("preview",preview);
// log.info("Anchors found",anchors);
if(!(mainVidId || nodes.length > 0 || preview)) return;
const ids = [mainVidId,...nodes.map(getVideoId).filter(id => id && !cachedTitles[id])];
// log.info("ids",ids);
if(ids.length <= 0) return;
const reqUrl = `https://www.googleapis.com/youtube/v3/videos?part=snippet&id=${ids.join(",")}&key=${apiKey}`;
try {
const res = await fetch(reqUrl);
const json = await res.json();
if(json.kind == "youtube#videoListResponse")
{
const {items} = json;
if(mainVidId)
{
const item = items.find(it => it.id == mainVidId);
console.log("main item",item);
if(item)
{
const {snippet} = item;
const title = snippet.title?.trim();
//Revert main title
const mainTitleNode = document.querySelector("ytd-watch-flexy:not([hidden]) #container > h1 > yt-formatted-string");
console.log("mainTitleNode",mainTitleNode);
if(mainTitleNode && title)
{
log.info(`Reverting main video title from ${mainTitleNode.innerHTML} to ${title}`);
mainTitleNode.innerHTML = title;
mainTitleNode.removeAttribute("is-empty");
//Revert description
const descriptionNode = document.querySelector("yt-formatted-string.content.style-scope.ytd-video-secondary-info-renderer");
if(descriptionNode && snippet.description)
{
descriptionNode.innerHTML = linkify(snippet.description);
}
isMainChanged = true;
}
}
}
for(let item of items)
{
cachedTitles[item.id] = item?.snippet?.title;
}
for(let node of nodes)
{
const cachedTitle = cachedTitles[getVideoId(node)];
if(cachedTitle)
{
const translatedTitle = node.innerHTML.trim();
if(translatedTitle != cachedTitle.replace(/\s{2,}/g, " "))
{
log.info(`Reverting ${translatedTitle} to ${cachedTitle}`);
node.innerHTML = cachedTitle;
}
if(!revertedAnchors.has(node))
{
revertedAnchors.add(node);
}
}
}
if(preview)
{
const id = getVideoId(preview);
const cachedTitle = cachedTitles[id];
if(cachedTitle)
{
const translatedTitle = preview.innerHTML.trim();
if(translatedTitle != cachedTitle.replace(/\s{2,}/g," "))
{
log.info(`Reverting preview ${translatedTitle} to ${cachedTitle}`);
preview.innerHTML = cachedTitle;
isPreviewChanged = true;
}
}
}
}
else
{
log.error("API request failed");
noApiKey = !isApiKeyValid;
if(noApiKey)
{
// GM.deleteValue(API_KEY);
log.error("API key fail. Please Reload");
}
}
} catch (err) {
log.error(err);
}
}
(async ()=>{
'use strict';
const apiKey = await getApiKey();
noApiKey = !apiKey;
if(noApiKey)
{
log.error("No API key");
return;
}
// log.info("API key:",apiKey);
setInterval(() => {
untranslate(apiKey).catch(err => log.error(err));
}, 1000);
})();