// ==UserScript== // @name YouTube 字幕下载助手|CC字幕/翻译字幕/双语字幕 💬|Subtitle Downloader 📝 // @name:en YouTube Subtitle Downloader | CC Captions & Transcript Export 💬📝 // @name:ja YouTube字幕ダウンローダー|CC字幕・翻訳字幕・文字起こし 💬📝 // @name:es Descargador de Subtítulos YouTube | CC y Transcripción 💬📝 // @name:pt Baixador de Legendas YouTube | CC e Transcrição 💬📝 // @namespace https://ytsubtitle.tools // @version 1.0 // @description YouTube字幕下载神器,支持CC字幕/自动翻译字幕/双语字幕下载,支持SRT/TXT格式导出,支持Shorts字幕提取,完全免费无广告 // @description:en Download YouTube subtitles & CC captions easily. Auto-translated subtitles, bilingual captions, SRT/TXT export, Shorts captions. Free & no ads // @description:ja YouTube字幕を簡単ダウンロード。CC字幕・自動翻訳字幕・バイリンガル字幕対応、SRT/TXT形式エクスポート、ショート字幕対応、完全無料&広告なし // @description:es Descarga subtítulos de YouTube fácilmente. Subtítulos CC, traducción automática, subtítulos bilingües, exportación SRT/TXT, subtítulos de Shorts, gratis y sin anuncios // @description:pt Baixe legendas do YouTube facilmente. Legendas CC, tradução automática, legendas bilíngues, exportação SRT/TXT, legendas de Shorts, gratuito e sem anúncios // @author SubtitleTools // @license MIT // @match *://*.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @grant GM_addStyle // @connect www.ssyoutube.com // @run-at document-start // @supportURL https://saveanyyoutube.com // @compatible chrome // @compatible firefox // @compatible opera // @compatible edge // @compatible safari // @keywords youtube, subtitle, subtitles, caption, captions, cc, closed caption, transcript, srt, download subtitle, export caption, bilingual subtitle, auto translate, 字幕, 字幕下载, CC字幕, 双语字幕, 翻译字幕, YouTube字幕, 字幕提取, 油管字幕, 字幕导出, 字幕, ダウンロード, CC字幕, 翻訳字幕, 文字起こし, YouTube字幕, subtítulos, leyenda, transcripción, descargar subtítulos, legendas, transcrição, baixar legendas, 유튜브, 자막, 자막다운로드, CC자막, 번역자막 // @downloadURL https://update.greasyfork.icu/scripts/570780/YouTube%20%E5%AD%97%E5%B9%95%E4%B8%8B%E8%BD%BD%E5%8A%A9%E6%89%8B%EF%BD%9CCC%E5%AD%97%E5%B9%95%E7%BF%BB%E8%AF%91%E5%AD%97%E5%B9%95%E5%8F%8C%E8%AF%AD%E5%AD%97%E5%B9%95%20%F0%9F%92%AC%EF%BD%9CSubtitle%20Downloader%20%F0%9F%93%9D.user.js // @updateURL https://update.greasyfork.icu/scripts/570780/YouTube%20%E5%AD%97%E5%B9%95%E4%B8%8B%E8%BD%BD%E5%8A%A9%E6%89%8B%EF%BD%9CCC%E5%AD%97%E5%B9%95%E7%BF%BB%E8%AF%91%E5%AD%97%E5%B9%95%E5%8F%8C%E8%AF%AD%E5%AD%97%E5%B9%95%20%F0%9F%92%AC%EF%BD%9CSubtitle%20Downloader%20%F0%9F%93%9D.meta.js // ==/UserScript== ;(function ytSubtitleHelper() { "use strict"; /** * YouTube Subtitle & Caption Downloader * Injects a caption-download trigger into YouTube watch and Shorts pages. * Handles SPA navigation via yt-navigate-finish / yt-page-data-updated events. */ let domWatcher = null; let retryInterval = null; let routeChangeDelay = null; let framePolling = false; GM_addStyle(` .ytcc-subtitle-action { cursor: pointer; font-size: 13px; font-weight: 500; white-space: nowrap; color: #0f0f0f; background-color: #f2f2f2; border: 0; border-radius: 18px; padding: 0 15px; height: 36px; margin-inline-end: 8px; } .ytcc-subtitle-action:hover { background-color: #e6e6e6; } `); const locale = { "zh": { actionLabel: "CC 字幕下载", error: { watchPageError: "添加字幕按钮时出错:", shortsPageError: "添加Shorts字幕按钮时出错:" } }, "en": { actionLabel: "Get Subtitles", error: { watchPageError: "Error adding subtitle button:", shortsPageError: "Error adding Shorts subtitle button:" } }, "ja": { actionLabel: "字幕を取得", error: { watchPageError: "字幕ボタンの追加エラー:", shortsPageError: "Shorts字幕ボタンの追加エラー:" } }, "es": { actionLabel: "Obtener Subtítulos", error: { watchPageError: "Error al agregar botón de subtítulos:", shortsPageError: "Error al agregar botón de subtítulos Shorts:" } }, "pt": { actionLabel: "Obter Legendas", error: { watchPageError: "Erro ao adicionar botão de legendas:", shortsPageError: "Erro ao adicionar botão de legendas Shorts:" } } }; function getCurrentPageType() { const path = window.location.pathname; if (path.includes("/watch")) return "watch"; if (path.includes("/shorts/")) return "shorts"; return null; } function buildSubtitleTrigger() { const subtitleBtn = document.createElement("button"); subtitleBtn.className = "ytcc-subtitle-action"; subtitleBtn.textContent = locale["zh"].actionLabel; subtitleBtn.addEventListener("click", function () { const pageAddress = window.location.href; const targetHosts = ["saveanyyoutube.com"]; const selectedHost = targetHosts[Math.floor(Math.random() * targetHosts.length)]; const redirectLink = pageAddress.replace("youtube.com", selectedHost); window.open(redirectLink, "_blank"); }); return subtitleBtn; } function teardownListeners() { if (domWatcher) { domWatcher.disconnect(); domWatcher = null; } if (retryInterval) { clearTimeout(retryInterval); retryInterval = null; } } function placeActionElement() { const pageType = getCurrentPageType(); if (!pageType) return false; if (pageType === "watch") { const visibleMeta = document.querySelector( "ytd-watch-metadata:not([hidden]), ytd-video-primary-info-renderer:not([hidden])" ); if (!visibleMeta) return false; const hostElement = visibleMeta.querySelector("#top-level-buttons-computed"); if (!hostElement) return false; if (hostElement.parentElement.querySelector(".ytcc-subtitle-action")) return true; const subtitleBtn = buildSubtitleTrigger(); hostElement.insertAdjacentElement("beforebegin", subtitleBtn); return true; } if (pageType === "shorts") { const currentReel = document.querySelector("ytd-reel-video-renderer[is-active]"); if (!currentReel) return false; const hostElement = currentReel.querySelector("#actions-inner"); if (!hostElement) return false; if (currentReel.querySelector(".ytcc-subtitle-action")) return true; const subtitleBtn = buildSubtitleTrigger(); subtitleBtn.style.margin = "8px 0"; subtitleBtn.style.width = "48px"; subtitleBtn.style.height = "48px"; subtitleBtn.style.borderRadius = "50%"; subtitleBtn.style.display = "flex"; subtitleBtn.style.alignItems = "center"; subtitleBtn.style.justifyContent = "center"; subtitleBtn.innerHTML = ''; hostElement.insertAdjacentElement("afterbegin", subtitleBtn); return true; } return false; } function animFrameCheck(initTimestamp) { if (!framePolling) return; if (Date.now() - initTimestamp > 10000) { framePolling = false; return; } placeActionElement(); requestAnimationFrame(function () { animFrameCheck(initTimestamp); }); } function handleRouteChange() { teardownListeners(); if (!getCurrentPageType()) return; placeActionElement(); if (document.body) { domWatcher = new MutationObserver(function (mutations) { if (mutations.some(function (m) { return m.addedNodes.length; })) { placeActionElement(); } }); domWatcher.observe(document.body, { childList: true, subtree: true }); } framePolling = true; animFrameCheck(Date.now()); let checkCount = 0; function scheduleRetry() { retryInterval = setTimeout(function () { checkCount += 1; placeActionElement(); if (checkCount < 40) { scheduleRetry(); } else { retryInterval = null; } }, 500); } scheduleRetry(); } function onPageTransition() { placeActionElement(); if (routeChangeDelay) clearTimeout(routeChangeDelay); routeChangeDelay = setTimeout(function () { routeChangeDelay = null; handleRouteChange(); }, 100); } // Initialization: handle direct page load first if (document.body) { onPageTransition(); } else { document.addEventListener("DOMContentLoaded", onPageTransition); } // YouTube SPA navigation events document.addEventListener("yt-page-data-updated", onPageTransition); document.addEventListener("yt-navigate-finish", onPageTransition); })();