您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add playback speed controls to plex web player with keyboard shortcuts
当前为
// ==UserScript== // @name Plex Playback Speed // @namespace https://github.com/ZigZagT // @version 1.3.1 // @description Add playback speed controls to plex web player with keyboard shortcuts // @author ZigZagT // @include /^https?://[^/]*plex[^/]*/ // @include /^https?://[^/]*:32400/ // @match https://app.plex.tv/ // @match https://plex.tv/ // @license MIT // ==/UserScript== (function() { 'use strict'; const console_log = (...args) => { console.log(`PlexPlaybackSpeed:`, ...args) } const cycleSpeeds = [ 0.5, 0.8, 1, 1.2, 1.4, 1.6, 1.8, 2, 2.5, 3, 3, 5, 4, 5, 6, 7, 8, 9, 10, 15, 20 ]; const quickSetSpeeds = { 1: 1, 2: 1.5, 3: 2, 4: 3, 5: 4, 6: 5, 7: 7, 8: 8, 9: 10, }; function prompt(txt) { const existingPrompt = document.querySelector("#playback-speed-prompt"); if (existingPrompt) { document.body.removeChild(existingPrompt); } const prompt = document.createElement("div"); prompt.id = "playback-speed-prompt"; prompt.innerText = txt; document.body.appendChild(prompt); prompt.style = ` position: fixed; top: 0; left: 0; width: 8em; height: 2em; background-color: rgba(0, 0, 0, 0.5); color: white; font-size: 2em; text-align: center; z-index: 99999; pointer-events: none; `; setTimeout(() => { try { document.body.removeChild(prompt); } catch (e) {} }, 2000); } function getNextCycleSpeed(direction, currentSpeed) { let newSpeed = currentSpeed; for (const speed of cycleSpeeds) { if (direction === 'slowdown') { if (speed < currentSpeed) { newSpeed = speed; } else { break; } } else if (direction === 'speedup') { if (speed > currentSpeed) { newSpeed = speed; break; } } else { console.error(`invalid change speed direction ${direction}`) break; } } return newSpeed; } function keyboardUpdateSpeed(e) { const videoElem = document.querySelector("video"); if (videoElem == null) { return; } const currentSpeed = videoElem.playbackRate; let newSpeed = currentSpeed; console_log({currentSpeed, key: e.key}); if (e.key in quickSetSpeeds) { newSpeed = quickSetSpeeds[e.key]; } else if (["<", ","].includes(e.key)) { newSpeed = getNextCycleSpeed('slowdown', currentSpeed); } else if ([">", "."].includes(e.key)) { newSpeed = getNextCycleSpeed('speedup', currentSpeed); } else { return; } console_log('change speed to', newSpeed); videoElem.playbackRate = newSpeed; prompt(`Speed: ${newSpeed}x`); } function btnSpeedUpFn() { const currentSpeed = document.querySelector("video").playbackRate; let newSpeed = getNextCycleSpeed('speedup', currentSpeed); console_log('change speed to', newSpeed); document.querySelector("video").playbackRate = newSpeed; prompt(`Speed: ${newSpeed}x`); } function btnSlowdownFn() { const currentSpeed = document.querySelector("video").playbackRate; let newSpeed = getNextCycleSpeed('slowdown', currentSpeed); console_log('change speed to', newSpeed); document.querySelector("video").playbackRate = newSpeed; prompt(`Speed: ${newSpeed}x`); } function addPlaybackButtonControls() { const btnStyle = ` align-items: center; border-radius: 15px; display: flex; font-size: 18px; height: 30px; justify-content: center; margin-left: 5px; text-align: center; width: 30px; `; const containers = document.querySelectorAll('[class*="PlayerControls-buttonGroupRight"]'); containers.forEach(container => { if (container.querySelector('#playback-speed-btn-slowdown')) { return; } const btnSlowDown = document.createElement('button'); btnSlowDown.id = 'playback-speed-btn-slowdown'; btnSlowDown.style = btnStyle; btnSlowDown.innerHTML = '🐢'; btnSlowDown.addEventListener('click', btnSlowdownFn); const btnSpeedUp = document.createElement('button'); btnSpeedUp.id = 'playback-speed-btn-speedup'; btnSpeedUp.style = btnStyle; btnSpeedUp.innerHTML = '🐇'; btnSpeedUp.addEventListener('click', btnSpeedUpFn); console_log('adding speed controls to', container); container.prepend(btnSlowDown, btnSpeedUp); }) } function scheduleLoopFrame() { setTimeout(() => { requestAnimationFrame(() => { addPlaybackButtonControls(); scheduleLoopFrame(); }); }, 500); } if (window.__plex_playback_speed_control_registered__) { console_log('plex playback speed controls are already registered'); } else { window.__plex_playback_speed_control_registered__ = true; console_log('registering plex playback speed controls'); window.addEventListener("keydown", keyboardUpdateSpeed); scheduleLoopFrame(); } })();