您需要先安装一个扩展,例如 篡改猴、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() }) // переиницализация при обновлении страницы без перезагрузки скрипта
- })();