feat: support subtitle translate for userscript
This commit is contained in:
@@ -91,7 +91,6 @@ const userscriptWebpack = (config, env) => {
|
||||
// @grant GM.getValue
|
||||
// @grant GM.deleteValue
|
||||
// @grant GM.info
|
||||
// @grant GM.addElement
|
||||
// @grant unsafeWindow
|
||||
// @connect translate.googleapis.com
|
||||
// @connect translate-pa.googleapis.com
|
||||
@@ -132,7 +131,6 @@ const userscriptWebpack = (config, env) => {
|
||||
config.entry = {
|
||||
main: paths.appIndexJs,
|
||||
options: paths.appSrc + "/options.js",
|
||||
injector: paths.appSrc + "/injector.js",
|
||||
"kiss-translator.user": paths.appSrc + "/userscript.js",
|
||||
};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import { trySyncAllSubRules } from "./libs/subRules";
|
||||
import { isInBlacklist } from "./libs/blacklist";
|
||||
import { runSubtitle } from "./subtitle/subtitle";
|
||||
import { logger } from "./libs/log";
|
||||
import { injectInlineJs } from "./libs/injector";
|
||||
|
||||
/**
|
||||
* 油猴脚本设置页面
|
||||
@@ -35,9 +36,10 @@ function runSettingPage() {
|
||||
const ping = genEventName();
|
||||
window.addEventListener(ping, handlePing);
|
||||
// window.eval(`(${injectScript})("${ping}")`); // eslint-disable-line
|
||||
const script = document.createElement("script");
|
||||
script.textContent = `(${injectScript})("${ping}")`;
|
||||
document.head.append(script);
|
||||
injectInlineJs(
|
||||
`(${injectScript})("${ping}")`,
|
||||
"kiss-translator-options-injector"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,19 +1,3 @@
|
||||
(function () {
|
||||
const originalOpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function (...args) {
|
||||
const url = args[1];
|
||||
if (typeof url === "string" && url.includes("timedtext")) {
|
||||
this.addEventListener("load", function () {
|
||||
window.postMessage(
|
||||
{
|
||||
type: "KISS_XHR_DATA_YOUTUBE",
|
||||
url: this.responseURL,
|
||||
response: this.responseText,
|
||||
},
|
||||
window.location.origin
|
||||
);
|
||||
});
|
||||
}
|
||||
return originalOpen.apply(this, args);
|
||||
};
|
||||
})();
|
||||
import { XMLHttpRequestInjector } from "./subtitle/XMLHttpRequestInjector";
|
||||
|
||||
XMLHttpRequestInjector();
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
// Function to inject inline JavaScript code
|
||||
export const injectInlineJs = (code) => {
|
||||
const el = document.createElement("script");
|
||||
el.setAttribute("data-source", "kiss-inject injectInlineJs");
|
||||
el.setAttribute("type", "text/javascript");
|
||||
el.textContent = code;
|
||||
document.body?.appendChild(el);
|
||||
};
|
||||
import { trustedTypesHelper } from "./trustedTypes";
|
||||
|
||||
// Function to inject external JavaScript file
|
||||
export const injectExternalJs = (src, id = "kiss-translator-injector") => {
|
||||
// Function to inject inline JavaScript code
|
||||
export const injectInlineJs = (code, id = "kiss-translator-inline-js") => {
|
||||
if (document.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// const el = document.createElement("script");
|
||||
// el.setAttribute("data-source", "kiss-inject injectExternalJs");
|
||||
// el.setAttribute("type", "text/javascript");
|
||||
// el.setAttribute("src", src);
|
||||
// el.setAttribute("id", id);
|
||||
// document.body?.appendChild(el);
|
||||
const script = document.createElement("script");
|
||||
script.id = id;
|
||||
script.src = src;
|
||||
(document.head || document.documentElement).appendChild(script);
|
||||
const el = document.createElement("script");
|
||||
el.type = "text/javascript";
|
||||
el.id = id;
|
||||
el.textContent = trustedTypesHelper.createScript(code);
|
||||
(document.head || document.documentElement).appendChild(el);
|
||||
};
|
||||
|
||||
// Function to inject external JavaScript file
|
||||
export const injectExternalJs = (src, id = "kiss-translator-external-js") => {
|
||||
if (document.getElementById(id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const el = document.createElement("script");
|
||||
el.type = "text/javascript";
|
||||
el.id = id;
|
||||
el.src = trustedTypesHelper.createScriptURL(src);
|
||||
(document.head || document.documentElement).appendChild(el);
|
||||
};
|
||||
|
||||
// Function to inject internal CSS code
|
||||
|
||||
@@ -37,6 +37,7 @@ import { browser } from "./browser";
|
||||
import { isIframe, sendIframeMsg } from "./iframe";
|
||||
import { TransboxManager } from "./tranbox";
|
||||
import { InputTranslator } from "./inputTranslate";
|
||||
import { trustedTypesHelper } from "./trustedTypes";
|
||||
|
||||
/**
|
||||
* @class Translator
|
||||
@@ -1025,7 +1026,7 @@ export class Translator {
|
||||
translatedText,
|
||||
placeholderMap
|
||||
);
|
||||
const trustedHTML = this.#createTrustedHTML(htmlString);
|
||||
const trustedHTML = trustedTypesHelper.createHTML(htmlString);
|
||||
|
||||
// const parser = new DOMParser();
|
||||
// const doc = parser.parseFromString(trustedHTML, "text/html");
|
||||
@@ -1076,19 +1077,6 @@ export class Translator {
|
||||
}
|
||||
}
|
||||
|
||||
#createTrustedHTML(html) {
|
||||
if (window.trustedTypes && window.trustedTypes.createPolicy) {
|
||||
const policy = window.trustedTypes.createPolicy(
|
||||
"kiss-translator-policy#html",
|
||||
{
|
||||
createHTML: (input) => input,
|
||||
}
|
||||
);
|
||||
return policy.createHTML(html);
|
||||
}
|
||||
return html;
|
||||
}
|
||||
|
||||
// 处理节点转为翻译字符串
|
||||
#serializeForTranslation(nodes) {
|
||||
let replaceCounter = 0; // {{n}}
|
||||
@@ -1404,7 +1392,8 @@ export class Translator {
|
||||
injectJs && sendBgMsg(MSG_INJECT_JS, injectJs);
|
||||
injectCss && sendBgMsg(MSG_INJECT_CSS, injectCss);
|
||||
} else {
|
||||
injectJs && injectInlineJs(injectJs);
|
||||
injectJs &&
|
||||
injectInlineJs(injectJs, "kiss-translator-userinit-injector");
|
||||
injectCss && injectInternalCss(injectCss);
|
||||
}
|
||||
} catch (err) {
|
||||
|
||||
33
src/libs/trustedTypes.js
Normal file
33
src/libs/trustedTypes.js
Normal file
@@ -0,0 +1,33 @@
|
||||
export const trustedTypesHelper = (() => {
|
||||
const POLICY_NAME = "kiss-translator-policy";
|
||||
let policy = null;
|
||||
|
||||
if (globalThis.trustedTypes && globalThis.trustedTypes.createPolicy) {
|
||||
try {
|
||||
policy = globalThis.trustedTypes.createPolicy(POLICY_NAME, {
|
||||
createHTML: (string) => string,
|
||||
createScript: (string) => string,
|
||||
createScriptURL: (string) => string,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err.message.includes("already exists")) {
|
||||
policy = globalThis.trustedTypes.policies.get(POLICY_NAME);
|
||||
} else {
|
||||
console.error("cont create Trusted Types", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
createHTML: (htmlString) => {
|
||||
return policy ? policy.createHTML(htmlString) : htmlString;
|
||||
},
|
||||
createScript: (scriptString) => {
|
||||
return policy ? policy.createScript(scriptString) : scriptString;
|
||||
},
|
||||
createScriptURL: (urlString) => {
|
||||
return policy ? policy.createScriptURL(urlString) : urlString;
|
||||
},
|
||||
isEnabled: () => policy !== null,
|
||||
};
|
||||
})();
|
||||
19
src/subtitle/XMLHttpRequestInjector.js
Normal file
19
src/subtitle/XMLHttpRequestInjector.js
Normal file
@@ -0,0 +1,19 @@
|
||||
export const XMLHttpRequestInjector = () => {
|
||||
const originalOpen = XMLHttpRequest.prototype.open;
|
||||
XMLHttpRequest.prototype.open = function (...args) {
|
||||
const url = args[1];
|
||||
if (typeof url === "string" && url.includes("timedtext")) {
|
||||
this.addEventListener("load", function () {
|
||||
window.postMessage(
|
||||
{
|
||||
type: "KISS_XHR_DATA_YOUTUBE",
|
||||
url: this.responseURL,
|
||||
response: this.responseText,
|
||||
},
|
||||
window.location.origin
|
||||
);
|
||||
});
|
||||
}
|
||||
return originalOpen.apply(this, args);
|
||||
};
|
||||
};
|
||||
@@ -38,7 +38,6 @@ class YouTubeCaptionProvider {
|
||||
|
||||
initialize() {
|
||||
window.addEventListener("message", (event) => {
|
||||
if (event.source !== window) return;
|
||||
if (event.data?.type === MSG_XHR_DATA_YOUTUBE) {
|
||||
const { url, response } = event.data;
|
||||
if (url && response) {
|
||||
|
||||
@@ -5,6 +5,8 @@ import { DEFAULT_API_SETTING } from "../config/api.js";
|
||||
import { DEFAULT_SUBTITLE_SETTING } from "../config/setting.js";
|
||||
import { injectExternalJs } from "../libs/injector.js";
|
||||
import { logger } from "../libs/log.js";
|
||||
import { XMLHttpRequestInjector } from "./XMLHttpRequestInjector.js";
|
||||
import { injectInlineJs } from "../libs/injector.js";
|
||||
|
||||
const providers = [
|
||||
{ pattern: "https://www.youtube.com", start: YouTubeInitializer },
|
||||
@@ -19,18 +21,10 @@ export function runSubtitle({ href, setting, isUserscript }) {
|
||||
|
||||
const provider = providers.find((item) => isMatch(href, item.pattern));
|
||||
if (provider) {
|
||||
const id = "kiss-translator-xmlHttp-injector";
|
||||
if (isUserscript) {
|
||||
GM.addElement("script", {
|
||||
src: "https://github.com/fishjar/kiss-translator/blob/gh-pages/injector.js",
|
||||
// src: "http://127.0.0.1:8000/injector.js",
|
||||
type: "text/javascript",
|
||||
}).onload = function () {
|
||||
console.log(
|
||||
"Script successfully injected and loaded via GM_addElement."
|
||||
);
|
||||
};
|
||||
injectInlineJs(`(${XMLHttpRequestInjector})()`, id);
|
||||
} else {
|
||||
const id = "kiss-translator-injector";
|
||||
const src = browser.runtime.getURL("injector.js");
|
||||
injectExternalJs(src, id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user