Twitch Scroll Wheel Volume

Scroll wheel volume control

Installer dette script?
Skaberens foreslåede script

Du vil måske også kunne lide Youtube Better Player

Installer dette script
// ==UserScript==
// @name         Twitch Scroll Wheel Volume
// @description  Scroll wheel volume control
// @include      https://www.twitch.tv/*
// @include      /^https:\/\/(?!supervisor).*\.ext-twitch\.tv\/.*anchor=video_overlay.*$/
// @run-at       document-idle
// @allFrames    true
// @version 0.0.1.20240222233855
// @namespace https://greasyfork.org/users/286737
// ==/UserScript==

class Player {
    constructor() {
        this.playerTypeObserver = new MutationObserver(this.onPlayerTypeChange.bind(this))
        this.wheelVolume = new WheelVolume()
    }

    async init() {
        let $root

        while (!($root = $('.root-scrollable__wrapper'))) await wait(2000)

        this.$root = $root

        const onRootMutation = this.onRootMutation.bind(this)

        new MutationObserver(onRootMutation).observe($root, {childList: true})

        onRootMutation()
    }

    onRootMutation() {
        const $player = $('.persistent-player', this.$root)

        if ($player == this.$player) return

        this.$player = $player

        if ($player) this.onNewPlayer()
    }

    async onNewPlayer() {
        const api = this.api = await this.getApi()

        this.wheelVolume.init(api, this.get$eventCatcher(), this.get$volumeBar())

        this.$layout = $('.video-player', this.$player)

        this.playerTypeObserver.observe(this.$layout, {attributeFilter: ['data-a-player-type']})
    }

    async getApi() {
        const playerSel = 'div[data-a-target="player-overlay-click-handler"], .video-player'
        let $el, api

        while (!($el = $(playerSel, unsafeWindow.document))) await wait(2000)
        while (!(api = this.getReactPlayerApi($el))) await wait(500)

        return api
    }

    getReactPlayerApi($el) {
        let instance

        for (const key in $el) {
            if (key.startsWith('__reactInternalInstance$') || key.startsWith('__reactFiber$')) {
                instance = $el[key]
            }
        }

        let parent = instance.return

        for (let i = 0; i < 50; i++) {
            const player = parent.memoizedProps.mediaPlayerInstance

            if (player) return player.core

            parent = parent.return
        }
    }

    get$eventCatcher() {
        return $('.video-player__container', this.$player)
    }

    get$volumeBar() {
        return $('.video-ref .volume-slider__slider-container', this.$player)
    }

    onPlayerTypeChange() {
        if (this.$layout.dataset.aPlayerType == 'site') this.wheelVolume.$volumeBar = this.get$volumeBar()
    }
}

class WheelVolume {
    constructor() {
        this.onWheelHandler = this.onWheel.bind(this)
        this.onMousedownHandler = this.onMousedown.bind(this)

        this.events = {
            mouseover: new Event('mouseover', {bubbles: true}),
            mouseout: new Event('mouseout', {bubbles: true}),
            mouseenter: new Event('mouseenter')
        }

        const onExtMessage = this.onExtMessage.bind(this)

        addEventListener('message', onExtMessage)
    }

    init(api, $eventCatcher, $volumeBar) {
        this.api = api
        this.$eventCatcher = $eventCatcher
        this.$volumeBar = $volumeBar

        $eventCatcher.addEventListener('wheel', this.onWheelHandler)
        $eventCatcher.addEventListener('mousedown', this.onMousedownHandler)
    }

    onWheel(e) {
        e.preventDefault()
        e.stopImmediatePropagation()

        this.updateVolume(e.deltaY < 0)
    }

    onMousedown(e) {
        if (e.which != 2) return

        e.preventDefault()

        this.toggleMute()
    }

    onExtMessage(e) {
        const event = e.data.wheelEvent

        if (!event) return

        switch (event) {
            case 'up':
                this.updateVolume(true)
                break
            case 'down':
                this.updateVolume(false)
                break
            case 'click':
                this.toggleMute()
        }
    }

    updateVolume(shouldIncrease) {
        this.show()

        const api = this.api, volume = api.getVolume()

        if ((volume == 0 && !shouldIncrease) || (volume == 1 && shouldIncrease)) return

        const now = Date.now(), since = now - this.prevScrollDate
        const step = (shouldIncrease ? 1 : -1) * (since < 50 ? 4 : 1) * .01

        if (api.isMuted()) api.setMuted(false)

        api.setVolume(volume + step)

        this.prevScrollDate = now
    }

    toggleMute() {
        this.show()

        const api = this.api

        api.setMuted(!api.isMuted())
    }

    show() {
        const $volumeBar = this.$volumeBar, events = this.events

        this.$eventCatcher.dispatchEvent(events.mouseenter)

        clearTimeout(this.showTimeout)

        $volumeBar.dispatchEvent(events.mouseover)

        this.showTimeout = setTimeout(() => $volumeBar.dispatchEvent(events.mouseout), 1000)
    }
}

class ExtFrame {
    init() {
        const onWheel = this.onWheel.bind(this)
        const onMousedown = this.onMousedown.bind(this)

        addEventListener('wheel', onWheel, {passive: false})
        addEventListener('mousedown', onMousedown, {passive: false})
    }

    onWheel(e) {
        e.preventDefault()
        e.stopPropagation()

        this.sendEvent(e.deltaY < 0 ? 'up' : 'down')
    }

    onMousedown(e) {
        if (e.which != 2) return

        e.preventDefault()

        this.sendEvent('click')
    }

    sendEvent(name) {
        parent.postMessage({wheelEvent: name}, 'https://supervisor.ext-twitch.tv/')
    }
}

const init = async () => {
    if (location.host == 'www.twitch.tv') return new Player().init()

    new ExtFrame().init()
}


const $ = (sel, el = document) => el.querySelector(sel)

const wait = async (ms) => await new Promise(r => setTimeout(r, ms))


init()