Video Overlay Vanisher

A tool to eliminate web video player overlays with Shift+D.

目前為 2023-08-23 提交的版本,檢視 最新版本

// ==UserScript==
// @name         Video Overlay Vanisher
// @namespace    http://tampermonkey.net/
// @version      1.2.3
// @description  A tool to eliminate web video player overlays with Shift+D.
// @author       CY Fung
// @icon         https://upload.cc/i1/2021/07/04/xVgQrq.png
// @match        https://*/*
// @exclude      https://www.youtube.com/live_chat*
// @exclude      /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
// @exclude      https://chat.openai.com/*
// @exclude      https://login.*/*
// @exclude      https://account.*/*
// @grant        none
// @run-at       document-start
// @license      MIT
// ==/UserScript==
(function $$() {
    'use strict';

    const keyCombination = {
        key: 'KeyD',
        shift: true
    }

    if (document.documentElement == null) return window.requestAnimationFrame($$);

    console.log("userscript enabled - Don't Overlay Video Player !")

    function addStyle(styleText) {
        const styleNode = document.createElement('style');
        styleNode.textContent = styleText;
        document.documentElement.appendChild(styleNode);
        return styleNode;
    }


    // Your code here...

    addStyle(`

[userscript-no-overlay-on] [userscript-no-overlay-hoverable], [userscript-no-overlay-on] [userscript-no-overlay-hoverable] *:not([userscript-no-overlay-hoverable]){
    visibility: collapse !important;
}

`);

    var qElm_PossibleHoverByPosition = new WeakMap();

    let callbackA_cid = 0;
    let doList = [];
    // Callback function to execute when mutations are observed
    const callbackA = function (mutations, observer) {
        // Use traditional 'for loops' for IE 11
        for (const mutation of mutations) {
            const {
                addedNodes
            } = mutation;
            for (const s of addedNodes) {
                if (s.nodeType === 1) doList.push(s);
            }
        }
        if (doList.length == 0) return;
        if (callbackA_cid) return;
        callbackA_cid = setTimeout(callbackB, 100);
    };




    // Create an observer instance linked to the callback function
    const observer = new MutationObserver(callbackA);

    doList = [document.documentElement];
    callbackB();

    // Start observing the target node for configured mutations
    observer.observe(document.documentElement, {
        childList: true,
        subtree: true
    });


    function callbackBmicro1(qElm) {

        const qElmComputedStyle = getComputedStyle(qElm)
        const {
            position
        } = qElmComputedStyle;

        if (position == 'absolute' || position == 'fixed') {
            qElm_PossibleHoverByPosition.set(qElm, position);
        } else {
            qElm_PossibleHoverByPosition.delete(qElm);
        }

    }


    function callbackB() {

        if (!doList.length || !callbackA_cid) return;

        let doListCopy = [...doList]
        doList.length = 0;

        let elements = null;

        function allParents(elm) {
            let res = [];
            while (elm = elm.parentNode) res.push(elm);
            return res;
        }
        let possibleContainer = null;

        for (const addedNode of doListCopy) {
            if (!addedNode || !addedNode.parentNode) continue;
            let parents = allParents(addedNode);
            if (possibleContainer == null) {
                possibleContainer = parents;
            } else {
                possibleContainer = possibleContainer.filter(possibleParent => parents.indexOf(possibleParent) >= 0);
            }
            if (possibleContainer.length <= 1) break;
        }
        doListCopy.length = 0;


        possibleContainer = (possibleContainer ? possibleContainer[0] : null) || null;



        Promise.resolve().then(() => {

            //console.log('possibleContainer',possibleContainer)

            if (possibleContainer) elements = [...possibleContainer.querySelectorAll('*')]; else elements = [];

        }).then(() => new Promise(resolve => setTimeout(resolve, 100))).then(() => {

            return Promise.all(elements.map(qElm => Promise.resolve(qElm).then(callbackBmicro1)));

        }).then(() => {


            //console.log('done', doList.length)

            if (doList.length > 0) {
                callbackA_cid = setTimeout(callbackB, 100);

            } else {
                callbackA_cid = 0;
            }

        })




    }

    let overlayHoverTID = 0;

    let resizeObserver = null;

    function resizeCallback(mutations) {

        //document.documentElement.removeAttribute('userscript-no-overlay-on')
        //overlayHoverTID=(overlayHoverTID+1)%2;

        if (!document.documentElement.hasAttribute('userscript-no-overlay-on')) {

            if (resizeObserver) {
                resizeObserver.disconnect();
                resizeObserver = null;
            }
            return;
        }

        let video = mutations[0].target;
        if (!video) return;

        makeHide(video);
    }

    function makeHide(videoElm) {

        if (!videoElm) return;
        videoElm.setAttribute('ve291', '');

        let _overlayHoverTID = overlayHoverTID;
        overlayHoverTID = (overlayHoverTID + 1) % 2;

        let rElms = [];

        for (const qElm of document.querySelectorAll('*')) {
            if (qElm_PossibleHoverByPosition.has(qElm)) rElms.push(qElm);
        }

        let replacementTexts = [];

        function replaceAll(str) {

            for (const s of replacementTexts) {
                if (str.length < s.length) continue;
                str = str.replace(s, '');
            }

            return str.trim();

        }

        var finalBoundaries = [];

        function getBoundaryElm() {

            finalBoundaries.length = 0;

            let _boundaryElm = videoElm;
            let boundaryElm = videoElm;
            while (_boundaryElm && replaceAll(_boundaryElm.textContent || '') == replaceAll(videoElm.textContent || '')) {
                boundaryElm = _boundaryElm;
                finalBoundaries.push(boundaryElm);
                _boundaryElm = _boundaryElm.parentNode;
            }

            return boundaryElm;
        }

        for (const s of rElms) {
            if (s.contains(videoElm)) continue;
            let sText = s.textContent;
            if (sText && sText.length > 0) replacementTexts.push(sText);
        }
        replacementTexts.sort((b, a) => a.length > b.length ? 1 : a.length < b.length ? -1 : 0);

        getBoundaryElm();

        let breakControl = false;

        while (!breakControl) {


            // youtube: boundary element (parent container) with no size.
            // ensure boundary element is larger than the child.
            var finalBoundaries_entries = finalBoundaries.map(elm => ({
                elm,
                rect: elm.getBoundingClientRect()
            }))

            for (const entry of finalBoundaries_entries) entry.size = Math.round(entry.rect.width * entry.rect.height || 0);

            let maxSize = Math.max(...finalBoundaries_entries.map(entry => entry.size))

            if (!maxSize) continue;

            finalBoundaries_entries = finalBoundaries_entries.filter(entry => entry.size == maxSize);

            let bmElm = finalBoundaries_entries[finalBoundaries_entries.length - 1].elm; // outest largest size

            let bRect = bmElm.getBoundingClientRect();

            for (const s of rElms) {

                if (s.contains(videoElm)) continue;

                let sRect = s.getBoundingClientRect();
                if (bRect && sRect) {
                    if (sRect.width * sRect.height > 0) {
                        if (sRect.left > bRect.right) continue;
                        if (sRect.top > bRect.bottom) continue;
                        if (sRect.right < bRect.left) continue;
                        if (sRect.bottom < bRect.top) continue;
                    } else {
                        continue;
                    }
                }

                s.setAttribute('userscript-no-overlay-hoverable', overlayHoverTID);
            }

            breakControl = true;

        }


        for (const s of document.querySelectorAll(`[userscript-no-overlay-hoverable="${_overlayHoverTID}"]`)) s.removeAttribute('userscript-no-overlay-hoverable');


    }


    function getVideoState() {

        let video = null;

        let videoElms = document.querySelectorAll('video');
        if (!videoElms.length) {
            return null;
        }

        let videos = [...videoElms].map(elm => ({
            elm,
            width: elm.offsetWidth,
            height: elm.offsetHeight
        }));

        let maxWidth = Math.max(...videos.map(item => item.width));
        let maxHeight = Math.max(...videos.map(item => item.height));

        if (maxWidth > 0 && maxHeight > 0) {

            video = videos.filter(item => item.width == maxWidth && item.height == maxHeight)[0] || null;

        }

        return video;

    }

    function postMessage(target, message, origin){
        let win = null;
        if(target instanceof HTMLIFrameElement){
            win = target.contentWindow;
        }else if( target && 'postMessage' in target){
            win = target;
        }
        if(!origin) origin = '*';
        if(win && typeof win.postMessage == 'function' ){
            try{
                win.postMessage(message, origin);
            }catch(e){}
        }
    }

    function spreadMessage() {

        for (const iframe of document.getElementsByTagName('iframe')) {
            if(+iframe.getAttribute('ve944')===mouseEnteredIframeIId ){
                postMessage(iframe, 'do-video-controls-hidden991');
            }
        }

    }

    function tryUnhide(){


        if (document.documentElement.hasAttribute('userscript-no-overlay-on')) {

            document.documentElement.removeAttribute('userscript-no-overlay-on')

            for (const s of document.querySelectorAll('[userscript-no-overlay-hoverable]')) {
                s.removeAttribute('userscript-no-overlay-hoverable');
            }

            const videoTarget = document.querySelector('[ve291]');

            if (videoTarget) {

                videoTarget.removeAttribute('ve291');


/*
                requestAnimationFrame(() => {
                    console.log(12321);

                    // Create a new mouse event
                    let event = new MouseEvent('mousemove', {
                        bubbles: true,
                        cancelable: true,
                        clientX: 100,
                        clientY: 100
                      });

                    // Dispatch the event to the element
                    videoTarget.dispatchEvent(event);
                })
                */

                return true;

            }


        }
        return false;

    }


    document.addEventListener('keydown', function (evt) {


        if (evt && evt.code == keyCombination.key && evt.shiftKey === keyCombination.shift) {

            let evtTarget = evt.target
            if (evtTarget.nodeType == 1) {
                if (evtTarget.nodeName == 'INPUT' || evtTarget.nodeName == 'TEXTAREA' || evtTarget.hasAttribute('contenteditable')) return;
            }
            evtTarget = null;
            evt.preventDefault();
            evt.stopPropagation();
            evt.stopImmediatePropagation();

            if (!tryUnhide()) {

                let videoState = getVideoState();
                if (videoState === null) {
                    //
                    // console.log('Unable to find any video element. If it is inside Iframe, please click the video inside iframe first.')

                    spreadMessage();

                }
                if (videoState && videoState.elm instanceof HTMLVideoElement) {
                    videoState.elm.dispatchEvent(new CustomEvent('video-controls-hidden675'))
                }

            }


        }
    }, true)


    document.addEventListener('video-controls-hidden675', (evt) => {
        let targetVideo = evt.target;
        if (!(targetVideo instanceof HTMLVideoElement)) return;

        if (resizeObserver) {
            resizeObserver.disconnect();
            resizeObserver = null;
        }
        resizeObserver = new ResizeObserver(resizeCallback)
        resizeObserver.observe(targetVideo)

        requestAnimationFrame(() => makeHide(targetVideo))


        //for(const s of document.querySelectorAll(`[userscript-no-overlay-hoverable]:not([userscript-no-overlay-hoverable="${overlayHoverTID}"])`)) s.removeAttribute('userscript-no-overlay-hoverable');
        document.documentElement.setAttribute('userscript-no-overlay-on', '')

    }, true);

    let mouseEnteredVideoVId = 0;
    let mouseEnteredIframeIId = 0;

    let di = 0;
    let domWeakHash = new WeakMap();
    document.addEventListener('mouseenter', (evt) => {
        if (evt && evt.target instanceof HTMLVideoElement) {
            const videoElm = evt.target;
            if (!domWeakHash.has(videoElm)) {
                let vid = ++di;
                domWeakHash.set(videoElm, vid);
                videoElm.setAttribute('ve944', vid);
            }
            mouseEnteredVideoVId = domWeakHash.get(videoElm);
        }else if(evt && evt.target instanceof HTMLIFrameElement){
            const iframeTarget = evt.target;
            if (!domWeakHash.has(iframeTarget)) {
                let vid = ++di;
                domWeakHash.set(iframeTarget, vid);
                iframeTarget.setAttribute('ve944', vid);
            }
            mouseEnteredIframeIId = +iframeTarget.getAttribute('ve944') || 0;
            postMessage(iframeTarget, 've761-iframe-entered')
        }
    }, true)
    document.addEventListener('mouseleave', (evt) => {

        if (evt && evt.target instanceof HTMLVideoElement) {
            const videoElm = evt.target;
            if (domWeakHash.has(videoElm)) {
                mouseEnteredVideoVId = 0;
            }
        }else if(evt && evt.target instanceof HTMLIFrameElement){

            const iframeTarget = evt.target;
            if (domWeakHash.has(iframeTarget)) {
                mouseEnteredIframeIId = 0;
            }
            postMessage(iframeTarget, 've762-iframe-leaved')
        }
    }, true)


    let isInIframeWindow = false;
    function receiveMessage(event) {
        if(!event) return;
        if (event.data === 'do-video-controls-hidden991') {
            let videoTarget = null;
            if (mouseEnteredVideoVId > 0 && isInIframeWindow > 0) {
                videoTarget = document.querySelector(`video[ve944="${mouseEnteredVideoVId}"]`);
            }
            if (!videoTarget) {
                if(isInIframeWindow){
                    spreadMessage();
                }
            } else {

                if(!tryUnhide()){
                    videoTarget.dispatchEvent(new CustomEvent('video-controls-hidden675'));

                }


            }
        }else if(event.data === 've761-iframe-entered'){
            isInIframeWindow = true;

        }else if(event.data === 've761-iframe-leaved'){
            isInIframeWindow = false;

        }
    }


    window.addEventListener('message', receiveMessage, false);






})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址