您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
增强 Bilibili 番剧的 CC 字幕,包含自动翻译及自动断行功能。
// ==UserScript== // @name Bilibili Subtitle Tweaks // @namespace Kr328/bilibili-subtitle-tweaks // @version 1.5 // @author Kr328 // @description 增强 Bilibili 番剧的 CC 字幕,包含自动翻译及自动断行功能。 // @license GPLv3 // @icon https://www.bilibili.com/favicon.ico // @match *://www.bilibili.com/bangumi/play/* // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/ajaxhook.min.js // @require https://cdn.jsdelivr.net/npm/[email protected]/dist/umd/t2cn.js // @grant unsafeWindow // @run-at document-start // ==/UserScript== (function (ajaxHook, OpenCC) { 'use strict'; function _interopNamespaceDefault(e) { const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } }); if (e) { for (const k in e) { if (k !== 'default') { const d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: () => e[k] }); } } } n.default = e; return Object.freeze(n); } const OpenCC__namespace = /*#__PURE__*/_interopNamespaceDefault(OpenCC); // src/native/alias.ts var _unsafeWindow = /* @__PURE__ */ (() => typeof unsafeWindow != "undefined" ? unsafeWindow : void 0)(); (() => { const translator = OpenCC__namespace.Converter({ from: "twp", to: "cn" }); function absoluteUrl(relativeUrl2, protocol) { if (!protocol) { protocol = "https:"; } return relativeUrl2.startsWith("//") ? protocol + relativeUrl2 : relativeUrl2; } function relativeUrl(absoluteUrl2) { const u = new URL(absoluteUrl2); return u.href.substring(u.protocol.length); } function formatable(text) { return text.replace("\n", "\\n"); } function log(msg) { console.log(`%c[SubtitleTweaks] ${msg}`, `color: #5bc6f4;`); } ajaxHook.proxy({ onRequest: (config, handler) => { const url = new URL(absoluteUrl(config.url)); switch (true) { case (url.hostname.endsWith("hdslb.com") && /bfs\/subtitle\/[a-z0-9]+\.json/.test(url.pathname)): { config.originUrl = url.toString(); config.transform = true; if (url.searchParams.has("translate")) { url.searchParams.delete("translate"); config.translate = true; } config.url = relativeUrl(url.toString()); break; } case (url.hostname === "api.bilibili.com" && ("/x/player/wbi/v2" == url.pathname || "/x/player/v2" == url.pathname)): { config.inject = true; break; } } handler.next(config); }, onResponse: (response, handler) => { if (response.config.transform === true) { const subtitleList = JSON.parse(response.response); log(`Transforming ${response.config.originUrl}`); subtitleList.body.forEach((value) => { const original = value.content; let result = original.replace(/\s[-—-]/, (s) => ` ${s.substring(1)}`); if (response.config.translate === true) { result = translator(result); } const formatTimestamp = (timestamp2, base) => { return (Math.trunc(timestamp2 / base) % (base * 60)).toString().padStart(2, "0"); }; const timestamp = `${formatTimestamp(value.from, 3600)}:${formatTimestamp(value.from, 60)}:${formatTimestamp(value.from, 1)}`; log(`${timestamp}: ${formatable(original)} => ${formatable(result)}`); value.content = result; }); response.response = JSON.stringify(subtitleList); } else if (response.config.inject === true) { log("Injecting"); const r = JSON.parse(response.response); const newSubtitles = r.data.subtitle?.subtitles?.flatMap((value, index) => { if (value.lan === "zh-Hant") { const translatedValue = JSON.parse(JSON.stringify(value)); translatedValue.lan = "zh-Hans"; translatedValue.lan_doc = "中文(简体) - 自动翻译"; translatedValue.id = index; translatedValue.id_str = translatedValue.id.toString(); const newUrl = new URL(absoluteUrl(translatedValue.subtitle_url)); newUrl.searchParams.append("translate", "1"); translatedValue.subtitle_url = relativeUrl(newUrl.toString()); return [translatedValue, value]; } return [value]; }); if (newSubtitles) { r.data.subtitle.subtitles = newSubtitles; response.response = JSON.stringify(r); } } handler.next(response); } }, _unsafeWindow); })(); })(ah, OpenCC);