diff --git a/plugins/VideoScrollWheel/VideoScrollWheel.yml b/plugins/VideoScrollWheel/VideoScrollWheel.yml new file mode 100644 index 00000000..359ac97c --- /dev/null +++ b/plugins/VideoScrollWheel/VideoScrollWheel.yml @@ -0,0 +1,37 @@ +name: VideoScrollWheel +description: Adds functionality to change volume/time in scene video player by hovering over left/right side of player and scrolling with mouse scrollwheel. Scroll while hovering on left side to adjust volume, scroll on right side to skip forward/back. +version: 0.1 +settings: + volumeScrollSpeed: + displayName: Volume Scroll Speed + description: (Default=100.0) Scales the amount of change in volume per mouse wheel click. Negative value reverses scroll direction. + type: NUMBER + timeScrollSpeed: + displayName: Time Scroll Speed + description: (Default=100.0) Scales the amount of change in time per mouse wheel click. Negative value reverses scroll direction. + type: NUMBER + timeScrollAcceleration: + displayName: Time Scroll Acceleration + description: (Default=100.0) Scales how quickly time scrolling accelerates from min speed to max speed. Set to 0 to disable. + type: NUMBER + minTimeScrollSpeed: + displayName: Min Time Scroll Velocity + description: (Default=1.0) When acceleration is enabled, scroll speed will increase from min velocity to max velocity. + type: NUMBER + maxTimeScrollSpeed: + displayName: Max Time Scroll Velocity + description: (Default=5.0) When acceleration is enabled, scroll speed will increase from min velocity to max velocity. + type: NUMBER + timeScrollVelocityDecay: + displayName: Time Scroll Velocity Decay + description: (Default=100.0) When acceleration is enabled, this value sets how quickly velocity returns to min value while not scrolling. + type: NUMBER + timeScrollVelocityTimeout: + displayName: Time Scroll Velocity Timeout + description: (Default=2000.0) When acceleration is enabled, velocity will reset to minimum after this number of milliseconds. + type: NUMBER +ui: + requires: + - StashUserscriptLibrary + javascript: + - videoScrollWheel.js diff --git a/plugins/VideoScrollWheel/videoScrollWheel.js b/plugins/VideoScrollWheel/videoScrollWheel.js new file mode 100644 index 00000000..f5bdde5f --- /dev/null +++ b/plugins/VideoScrollWheel/videoScrollWheel.js @@ -0,0 +1,119 @@ +(async () => { + while (!window.stash) { + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + const volumeScrollScale = -0.00065; + const timeScrollScale = 0.01; + const timeScrollFriction = 0.00015; + const timeScrollAcceleration = 0.55; + + let vjsPlayer = null; + let scrollVelocity = 1; + let previousTime = Date.now(); + let pluginSettings = { + volumeScrollSpeed: 100.0, + timeScrollSpeed: 100.0, + timeScrollAcceleration: 100.0, + minTimeScrollSpeed: 1.0, + maxTimeScrollSpeed: 5.0, + timeScrollVelocityDecay: 100.0, + timeScrollVelocityTimeout: 2000, + }; + + async function setupVideoScrollWheel() { + // Get settings + var settings = await getPluginConfig("videoScrollWheel"); + if (settings) { + for (var key in settings) { + if (pluginSettings.hasOwnProperty(key)) { + pluginSettings[key] = settings[key]; + } + } + } + + // Get video player and register wheel event listener. + vjsPlayer = document.getElementById("VideoJsPlayer").player; + var vjsEl = vjsPlayer.el_; + vjsEl.addEventListener("wheel", onWheel); + } + + function onWheel(e) { + // Get position of mouse within video player. + const target = e.target; + const targetWidth = target.offsetWidth; + const targetX = e.clientX - target.getBoundingClientRect().left; + + var scrollDelta = e.deltaY; + + if (targetWidth / 2 > targetX) { + // Scrolled on left side, change volume. + var newVolume = + vjsPlayer.volume() + + scrollDelta * + volumeScrollScale * + (pluginSettings.volumeScrollSpeed / 100.0); + vjsPlayer.volume(newVolume); + } else { + // Scrolled on right side, change time. + var now = Date.now(); + var deltaTime = now - previousTime; + if (deltaTime === 0) { + return; + } else if ( + deltaTime > pluginSettings.timeScrollVelocityTimeout || + pluginSettings.timeScrollAcceleration === 0 + ) { + scrollVelocity = pluginSettings.minTimeScrollSpeed; + } else { + var friction = + scrollVelocity * + timeScrollFriction * + deltaTime * + (pluginSettings.timeScrollVelocityDecay / 100.0); + var acceleration = + (1 / deltaTime) * + timeScrollAcceleration * + (pluginSettings.timeScrollAcceleration / 100.0); + scrollVelocity = scrollVelocity - friction + acceleration; + scrollVelocity = Math.max( + pluginSettings.minTimeScrollSpeed, + Math.min(scrollVelocity, pluginSettings.maxTimeScrollSpeed) + ); + } + + previousTime = now; + + var timeDelta = + scrollDelta * + timeScrollScale * + scrollVelocity * + (pluginSettings.timeScrollSpeed / 100.0); + var newTime = vjsPlayer.currentTime() + timeDelta; + vjsPlayer.currentTime(newTime); + } + } + + // Util functions for getting plugin settings. + async function getPluginConfigs() { + const reqData = { + operationName: "Configuration", + variables: {}, + query: `query Configuration { + configuration { + plugins + } + }`, + }; + return stash.callGQL(reqData); + } + async function getPluginConfig(pluginId) { + const data = await getPluginConfigs(); + return data.data.configuration.plugins[pluginId]; + } + + // Wait for video player to load on scene page. + stash.addEventListener("stash:page:scene", function () { + waitForElementId("VideoJsPlayer", setupVideoScrollWheel); + }); +})();