您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
YouTube双语字幕,任何语言翻译成中文。支持移动端和桌面端,适配Via浏览器。
当前为
// ==UserScript== // @name Youtube 双语字幕版 // @version 1.1 // @author LR // @license MIT // @description YouTube双语字幕,任何语言翻译成中文。支持移动端和桌面端,适配Via浏览器。 // @match *://www.youtube.com/* // @match *://m.youtube.com/* // @require https://unpkg.com/ajax-hook@latest/dist/ajaxhook.min.js // @grant none // @run-at document-start // @namespace https://greasyfork.org/users/1210499 // ==/UserScript== (function () { 'use strict'; const TARGET_LANG = 'zh'; // 设置目标翻译语言为中文 (async function enableDualSubtitles() { // 获取翻译后的字幕数据 async function fetchTranslatedSubtitles(url) { const cleanUrl = url.replace(/(^|[&?])tlang=[^&]*/g, '') + `&tlang=${TARGET_LANG}&translate_h00ked`; try { const response = await fetch(cleanUrl, { method: 'GET' }); if (!response.ok) { throw new Error(`Failed to fetch translated subtitles: ${response.status}`); } return await response.json(); } catch (error) { console.error(error); return null; } } function jaccardSimilarity(str1, str2) { const set1 = new Set(str1.split('')); const set2 = new Set(str2.split('')); const intersection = new Set([...set1].filter(x => set2.has(x))); const union = new Set([...set1, ...set2]); return intersection.size / union.size; } function mergeSubtitles(defaultSubs, translatedSubs) { const mergedSubs = JSON.parse(JSON.stringify(defaultSubs)); const translatedEvents = translatedSubs.events.filter(event => event.segs); let tIndex = 0; // 翻译事件索引 for (let i = 0; i < mergedSubs.events.length; i++) { const defaultEvent = mergedSubs.events[i]; if (!defaultEvent.segs) continue; // 匹配时间最接近的翻译字幕事件 while (tIndex < translatedEvents.length && translatedEvents[tIndex].tStartMs < defaultEvent.tStartMs) { tIndex++; } const translatedEvent = translatedEvents[tIndex]; if (translatedEvent) { const defaultText = defaultEvent.segs.map(seg => seg.utf8).join(''); const translatedText = translatedEvent.segs.map(seg => seg.utf8).join(''); // 使用 Jaccard 相似性来检测文本相似性 if (jaccardSimilarity(defaultText.trim(), translatedText.trim()) < 0.8) { defaultEvent.segs[0].utf8 = `${defaultText}\n${translatedText}`; defaultEvent.segs = [defaultEvent.segs[0]]; } } } return JSON.stringify(mergedSubs); } // 使用 ajax-hook 代理请求和响应,以获取并处理字幕数据 ah.proxy({ onResponse: async (response, handler) => { if (response.config.url.includes('/api/timedtext') && !response.config.url.includes('&translate_h00ked')) { try { const defaultSubs = JSON.parse(response.response); const translatedSubs = await fetchTranslatedSubtitles(response.config.url); if (translatedSubs) { response.response = mergeSubtitles(defaultSubs, translatedSubs); } } catch (error) { console.error("Error processing subtitles:", error); } } handler.resolve(response); } }); })(); })();