// ==UserScript==
// @name Video Overlay Vanisher
// @namespace http://tampermonkey.net/
// @version 1.4.0
// @description A tool to eliminate web video player overlays with Shift+D.
// @author CY Fung
// @icon https://na.cx/i/Hh10VGs.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://*.openai.com/*
// @exclude https://jsfiddle.net/*
// @exclude https://*.jsfiddle.net/*
// @exclude https://fiddle.*.net/*
// @exclude https://*.jshell.net/*
// @exclude https://fiddle.jshell.net/*
// @exclude https://login.*/*
// @exclude https://account.*/*
// @grant GM.getValue
// @run-at document-start
// @inject-into content
// @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();
var qElm_Cache = new WeakMap();
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;
callbackB(100);
};
function callbackBmicro1(qElm) {
if (!(qElm instanceof HTMLElement)) return;
if (qElm.isConnected === false) return;
let qElmComputedStyle = qElm_Cache.get(qElm);
if (!qElmComputedStyle) {
qElmComputedStyle = getComputedStyle(qElm)
qElm_Cache.set(qElm, qElmComputedStyle);
}
const {
position
} = qElmComputedStyle;
if (position == 'absolute' || position == 'fixed') {
qElm_PossibleHoverByPosition.set(qElm, position);
} else {
qElm_PossibleHoverByPosition.delete(qElm);
}
}
const createPipeline = () => {
let pipelineMutex = Promise.resolve();
const pipelineExecution = fn => {
return new Promise((resolve, reject) => {
pipelineMutex = pipelineMutex.then(async () => {
let res;
try {
res = await fn();
} catch (e) {
console.log("Pipeline Error", e);
reject(e);
}
resolve(res);
}).catch(console.warn);
});
};
return pipelineExecution;
}
const pipeline = createPipeline();
async function callbackB(delay) {
let res;
do {
res = await pipeline(async () => {
if (!doList.length) return;
if (delay > 0) {
await new Promise(resolve => setTimeout(resolve, delay));
}
if (!doList.length) return;
let doListCopy = doList.slice(0);
doList.length = 0;
function allParents(elm) {
let res = [];
while ((elm = elm.parentNode) instanceof HTMLElement) res.push(elm);
return res;
}
let possibleContainerSet = null;
const proceeded = new Set();
for (const addedNode of doListCopy) {
if (!addedNode || !addedNode.parentNode) continue;
if (addedNode.isConnected === false) continue;
if (proceeded.has(addedNode)) continue;
proceeded.add(addedNode);
const parentsSet = new Set(allParents(addedNode));
if (possibleContainerSet == null) {
possibleContainerSet = parentsSet;
} else {
for (const possibleParent of possibleContainerSet) {
if (!parentsSet.has(possibleParent)) possibleContainerSet.delete(possibleParent);
}
parentsSet.clear();
}
if (possibleContainerSet.size <= 1) break;
}
if(!possibleContainerSet) return;
proceeded.clear();
doListCopy.length = 0;
const possibleContainerSetIt = possibleContainerSet.values();
const possibleContainer = possibleContainerSetIt.next().value;
await Promise.resolve();
//console.log('possibleContainer',possibleContainer)
const elements = possibleContainer ? [...possibleContainer.querySelectorAll('*')] : null;
if (elements && elements.length >= 1) {
await new Promise(resolve => setTimeout(resolve, 100));
await Promise.all(elements.map(qElm => Promise.resolve(qElm).then(callbackBmicro1)));
}
//console.log('done', doList.length)
if (doList.length > 0) {
delay = 100;
return true;
}
});
} while (res === true);
}
// Create an observer instance linked to the callback function
const observer = new MutationObserver(callbackA);
doList = [document.documentElement];
callbackB(0);
// Start observing the target node for configured mutations
observer.observe(document.documentElement, {
childList: true,
subtree: true
});
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;
}
function keydownAsync() {
if (!tryUnhide()) {
const 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();
} else if (videoState && videoState.elm instanceof HTMLVideoElement) {
videoState.elm.dispatchEvent(new CustomEvent('video-controls-hidden675'))
}
}
}
document.addEventListener('keydown', function (evt) {
if (evt && evt.code == keyCombination.key && evt.shiftKey === keyCombination.shift) {
if (evt.isComposing) return;
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();
Promise.resolve().then(keydownAsync);
}
}, true);
let rafPromise = null;
const getRafPromise = () => rafPromise || (rafPromise = new Promise(resolve => {
requestAnimationFrame(hRes => {
rafPromise = null;
resolve(hRes);
});
}));
const controlsHidden675Async = async (targetVideo)=>{
if (resizeObserver) {
resizeObserver.disconnect();
resizeObserver = null;
}
resizeObserver = new ResizeObserver(resizeCallback)
resizeObserver.observe(targetVideo)
await getRafPromise();
makeHide(targetVideo);
document.documentElement.setAttribute('userscript-no-overlay-on', '');
}
document.addEventListener('video-controls-hidden675', (evt) => {
let targetVideo = evt.target;
if (!(targetVideo instanceof HTMLVideoElement)) return;
Promise.resolve(targetVideo).then(controlsHidden675Async);
}, 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 controlsHidden991Async() {
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'));
}
}
}
function receiveMessage(event) {
if (!event) return;
if (event.data === 'do-video-controls-hidden991') {
Promise.resolve().then(controlsHidden991Async);
} else if (event.data === 've761-iframe-entered') {
isInIframeWindow = true;
} else if (event.data === 've761-iframe-leaved') {
isInIframeWindow = false;
}
}
window.addEventListener('message', receiveMessage, false);
GM.getValue("dummy"); // dummy
})();