您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Saima extension customization ( https://saima.ai/ ): fast speed control WPM (words per minute); correct placement of the Saima button at the same time as the SponsorBlock button.
当前为
// ==UserScript== // @name Saima customizer // @name:ru Saima: кастомизация // @namespace http://tampermonkey.net/ // @version 1.0.0 // @description Saima extension customization ( https://saima.ai/ ): fast speed control WPM (words per minute); correct placement of the Saima button at the same time as the SponsorBlock button. // @description:ru Настройка расширения Saima ( https://saima.ai/ ): быстрый контроль скорости WPM (слов в минуту); правильное размещение кнопки Saima одновременно с кнопкой SponsorBlock. // @author Igor Lebedev // @license MIT // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @match http://*.youtube.com/* // @match https://*.youtube.com/* // @require https://openuserjs.org/src/libs/sizzle/GM_config.js // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-idle // ==/UserScript== //debugger; /* global GM_config */ (() => { 'use strict'; GM_config.init({ id: 'sc_config', title: GM_info.script.name + ' Settings', fields: { DEBUG_MODE: { label: 'Debug mode', type: 'checkbox', default: false, title: 'Log debug messages to the console' }, CHECK_FREQUENCY_LAUNCH_SAIMA: { label: 'Check frequency (ms) launch of the Saima extension', type: 'number', min: 1000, default: 5000, title: 'The number of milliseconds to wait between checking the launch of the Saima extension' }, CHECK_FREQUENCY_LAUNCH_SPONSOR_BLOCK: { label: 'Check frequency (ms) launch of the SponsorBlock extension', type: 'number', min: 1000, default: 5000, title: 'The number of milliseconds to wait between checking the launch of the Saima extension' }, WPM_SPEED: { label: 'WPM speed with one scroll of the wheel', type: 'number', min: 1, max: 400, default: 30, title: 'Changing the WPM (words per minute) speed with one scroll of the wheel' }, SPEED_INDICATOR_TRANSPARENT: { label: 'Transparency of the speed indicator', type: 'number', min: 0.1, max: 1, default: 0.3, title: 'Transparency of the speed indicator on the button' }, }, events: { init: onInit } }) GM_registerMenuCommand('Settings', () => { GM_config.open() }) class Debugger { constructor (name, enabled) { this.debug = {} if (!window.console) { return () => { } } Object.getOwnPropertyNames(window.console).forEach(key => { if (typeof window.console[key] === 'function') { if (enabled) { this.debug[key] = window.console[key].bind(window.console, name + ': ') } else { this.debug[key] = () => { } } } }) return this.debug } } var DEBUG const SELECTORS = { PLAYER: '#movie_player', StartSegmentButton: '#startSegmentButton', SaimaButtonContainer: '#saima-button-container', SaimaButtonContainer_button: '#saima-button-container > div > button', SaimaButtonContainer_button_canvas: '#saima-button-container > div > button > canvas', SaimaTextField: '.__saima__text-field', } const WPM_Min = 107 // Минимальная скорость (слов в минуту) const WPM_Max = 498 // Максимальная скорость (слов в минуту) const WPM_IntervalLenght = WPM_Max - WPM_Min // величина рабочего диапазаона скоростей WPM let CanvasMaskTransparent = 0.3 // прозрачность маски let WheelWPM = 30 // Изменение скорости WPM за одну прокрутку колёсика function onInit() { DEBUG = new Debugger(GM_info.script.name, GM_config.get('DEBUG_MODE')) let CanvasMaskTransparent = GM_config.get('SPEED_INDICATOR_TRANSPARENT') WheelWPM = GM_config.get('WPM_SPEED') Move_after_SponsorBlock() Quick_WPM_speed_control() } // Перестановка на плеере кнопки Saima правее кнокпи SponsorBlock - во избежание смещений кнопки Saima при наведении на кнопку SponsorBlock function Move_after_SponsorBlock(){ let jsInitChecktimer = null function isDownloadElements(evt) { function wait () { //ожидание загрузки страницы до необходимого значения if (watchThresholdReached()) { try { const StartSegmentButton = document.querySelector(SELECTORS.StartSegmentButton) const SaimaButtonContainer = document.querySelector(SELECTORS.SaimaButtonContainer) if (StartSegmentButton && SaimaButtonContainer) { // Перестановка StartSegmentButton.after(SaimaButtonContainer) clearInterval(jsInitChecktimer) jsInitChecktimer = null } } catch (e) { DEBUG.info(`Failed to like video: ${e}. `) } } } jsInitChecktimer = setInterval(wait, GM_config.get('CHECK_FREQUENCY_LAUNCH_SPONSOR_BLOCK')) } isDownloadElements() } // Быстрое изменение скорости WPM function Quick_WPM_speed_control() { let jsInitChecktimer2 = null function isDownloadStartSegmentButton(evt) { function wait () { //ожидание загрузки страницы до необходимого значения if (watchThresholdReached()) { try { const SaimaButtonContainer = document.querySelector(SELECTORS.SaimaButtonContainer) const SaimaTextField = document.querySelector(SELECTORS.SaimaTextField) if (SaimaButtonContainer && SaimaTextField) { const SaimaButtonContainer_button = document.querySelector(SELECTORS.SaimaButtonContainer_button) if (SaimaButtonContainer_button) { let SaimaButtonContainer_spanIco_SpeedProp = 1 - (SaimaTextField._value - WPM_Min) / WPM_IntervalLenght let canvas = document.createElement('canvas') let ctx = canvas.getContext('2d') // Предполагая, что размеры контейнера уже установлены canvas.width = SaimaButtonContainer_button.offsetWidth canvas.height = SaimaButtonContainer_button.offsetHeight canvas.style.position = 'absolute' // Добавляем эффект маски ctx.fillStyle = `rgba(0, 0, 0, ${CanvasMaskTransparent})` // ctx.fillRect(0, 0, canvas.width, canvas.height / 2) ctx.fillRect(0, 0, canvas.width, canvas.height * SaimaButtonContainer_spanIco_SpeedProp) // Добавляем canvas в контейнер SaimaButtonContainer_button.appendChild(canvas) } SaimaButtonContainer.addEventListener('wheel', function(event) { // Предотвращаем стандартное поведение прокрутки страницы event.preventDefault() let RepeatedPresses = 1 // Количество нажатий на одну прокрутку колеса мыши let TimeOutMS = 1 // Интервал между нажатиями // A function to simulate a button click function simulateClickWrap(sign) { function simulateClick() { let valueNew = SaimaTextField.valueAsNumber + sign * WheelWPM switch(sign) { case 1: if (valueNew > WPM_Max) valueNew = WPM_Max break case -1: if (valueNew < WPM_Min) valueNew = WPM_Min break } SaimaTextField.value = valueNew SaimaTextField._value = valueNew // Создаем новое событие 'input' let eventInput = new Event('input', { bubbles: true, // Событие будет всплывать вверх по DOM-дереву cancelable: false // Событие 'input' обычно не предполагает отмену его действий }); // Искусственно вызываем событие 'input' на элементе input SaimaTextField.dispatchEvent(eventInput) } if ((event.deltaY < 0 && SaimaTextField.valueAsNumber < WPM_Max) || (event.deltaY > 0 && SaimaTextField.valueAsNumber > WPM_Min)) { for (let i = 0; i < RepeatedPresses; i++) { setTimeout((function(index) { return function() { simulateClick() // console.log('Button clicked:', index); } })(i), i * TimeOutMS) // Задержка TimeOutMS между кликами } } const SaimaButtonContainer_button_canvas = document.querySelector(SELECTORS.SaimaButtonContainer_button_canvas) if (SaimaButtonContainer_button_canvas) { let SaimaButtonContainer_spanIco_SpeedProp = 1 - (SaimaTextField._value - WPM_Min) / WPM_IntervalLenght function updateMaskHeight(newHeightFraction) { let ctx = SaimaButtonContainer_button_canvas.getContext('2d') // Очистка холста ctx.clearRect(0, 0, SaimaButtonContainer_button_canvas.width, SaimaButtonContainer_button_canvas.height) // Перерисовка маски с новой высотой ctx.fillStyle = `rgba(0, 0, 0, ${CanvasMaskTransparent})` ctx.fillRect(0, 0, SaimaButtonContainer_button_canvas.width, SaimaButtonContainer_button_canvas.height * newHeightFraction) } // Обновление высоты маски от высоты холста updateMaskHeight(SaimaButtonContainer_spanIco_SpeedProp) } } // event.deltaY содержит значение, которое указывает направление прокрутки: // положительное значение для прокрутки вниз, отрицательное - вверх. if (event.deltaY < 0) { // console.log('Прокрутка вверх') // if (SaimaTextField.valueAsNumber < WPM_Max) simulateClickWrap(1) simulateClickWrap(1) } else { // console.log('Прокрутка вниз') // if (SaimaTextField.valueAsNumber > WPM_Min) simulateClickWrap(-1) simulateClickWrap(-1) } }) clearInterval(jsInitChecktimer2) jsInitChecktimer2 = null } } catch (e) { DEBUG.info(`Failed to like video: ${e}. `) } } } jsInitChecktimer2 = setInterval(wait, GM_config.get('CHECK_FREQUENCY_LAUNCH_SAIMA')) } isDownloadStartSegmentButton() } function watchThresholdReached () { const player = document.querySelector(SELECTORS.PLAYER) if (player) { return true } return false } // onInit() window.addEventListener("yt-navigate-start", e => { onInit() }) // переиницализация при обновлении страницы без перезагрузки скрипта })();