// ==UserScript== // @name YouTube 長按右鍵加速播放 // @namespace https://example.com/yt-hold-speedup // @version 2.0.2 // @description 短按「→」維持 YouTube 原本快轉 5 秒的行為;長按「→」時將影片加速播放,放開恢復原速;長按期間按「↑/↓」可調整加速倍率,並會跨影片、跨頁面持久保存 // @author jjjghu // @match https://www.youtube.com/* // @grant GM_getValue // @grant GM_setValue // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const STORAGE_KEY = 'yt_hold_speedup_multiplier'; const MIN_MULTIPLIER = 1; const MAX_MULTIPLIER = 16; const STEP = 0.25; // 判斷「長按」的門檻時間(毫秒)。超過這個時間還沒放開才算長按。 const LONG_PRESS_MS = 300; // 短按時要重現 YouTube 原本的快轉秒數(YouTube 預設方向鍵快轉為 5 秒) const SHORT_PRESS_SEEK_SECONDS = 5; // 讀取先前儲存的倍率,預設 3 倍 let multiplier = GM_getValue(STORAGE_KEY, 3); let isHolding = false; // 是否已經判定為「長按中」並套用加速 let originalRate = 1; // 按住前的原始播放速度 let pressTimer = null; // 用來判斷長按的計時器 let toastTimer = null; function getVideo() { // YouTube 觀看頁的主要 video 元素 return document.querySelector('video.html5-main-video') || document.querySelector('video'); } function isTypingContext(target) { if (!target) return false; const tag = target.tagName; if (tag === 'INPUT' || tag === 'TEXTAREA' || target.isContentEditable) return true; // YouTube 留言框、搜尋框等通常會落在上面條件內,這裡再保險判斷一下 if (target.closest && target.closest('#contenteditable-root')) return true; return false; } function clampMultiplier(v) { return Math.min(MAX_MULTIPLIER, Math.max(MIN_MULTIPLIER, v)); } function saveMultiplier(v) { multiplier = clampMultiplier(v); GM_setValue(STORAGE_KEY, multiplier); } function showToast(text) { let el = document.getElementById('yt-hold-speedup-toast'); if (!el) { el = document.createElement('div'); el.id = 'yt-hold-speedup-toast'; el.style.cssText = [ 'position:fixed', 'top:16px', 'left:50%', 'transform:translateX(-50%)', 'background:rgba(0,0,0,0.75)', 'color:#fff', 'padding:6px 14px', 'border-radius:6px', 'font-size:14px', 'font-family:Roboto,Arial,sans-serif', 'z-index:999999', 'pointer-events:none', 'transition:opacity 0.2s', ].join(';'); document.body.appendChild(el); } el.textContent = text; el.style.opacity = '1'; clearTimeout(toastTimer); toastTimer = setTimeout(() => { el.style.opacity = '0'; }, 800); } function startSpeedup(video) { isHolding = true; originalRate = video.playbackRate; video.playbackRate = multiplier; showToast(`加速中 x${multiplier}`); } function stopSpeedup(video) { isHolding = false; if (video) { video.playbackRate = originalRate; } } function doShortPressSeek(video) { // 手動重現 YouTube 原本「按一下右鍵快轉 5 秒」的行為 const duration = isFinite(video.duration) ? video.duration : Infinity; video.currentTime = Math.min(duration, video.currentTime + SHORT_PRESS_SEEK_SECONDS); } document.addEventListener( 'keydown', (e) => { if (isTypingContext(e.target)) return; const video = getVideo(); if (!video) return; if (e.code === 'ArrowRight') { // 一律先阻止 YouTube 預設行為,等放開時才決定是「短按快轉」還是「已經是長按加速」 e.preventDefault(); e.stopPropagation(); if (e.repeat) return; // 瀏覽器長按產生的重複 keydown,不用重新啟動計時器 // 開始計時,超過門檻還沒放開才視為長按、觸發加速 pressTimer = setTimeout(() => { pressTimer = null; startSpeedup(video); }, LONG_PRESS_MS); return; } // 長按右鍵期間,用上下鍵調整倍率 if (isHolding && (e.code === 'ArrowUp' || e.code === 'ArrowDown')) { e.preventDefault(); e.stopPropagation(); if (e.repeat) return; // 避免長按上下鍵時倍率跳太快,可依喜好拿掉 const delta = e.code === 'ArrowUp' ? STEP : -STEP; saveMultiplier(multiplier + delta); video.playbackRate = multiplier; showToast(`加速倍率 x${multiplier}`); } }, true ); document.addEventListener( 'keyup', (e) => { if (e.code !== 'ArrowRight') return; const video = getVideo(); if (pressTimer) { // 還沒到長按門檻就放開了 → 判定為「短按」,重現原本的快轉 5 秒行為 clearTimeout(pressTimer); pressTimer = null; if (video) doShortPressSeek(video); return; } if (isHolding) { // 已經進入長按加速狀態 → 放開時恢復原速 stopSpeedup(video); } }, true ); // 保險:如果視窗失焦(例如切換分頁)時仍處於加速或計時狀態,恢復原狀避免卡住 window.addEventListener('blur', () => { if (pressTimer) { clearTimeout(pressTimer); pressTimer = null; } if (isHolding) { stopSpeedup(getVideo()); } }); })();