Force native HTML5 controls on every video on 9GAG
// ==UserScript==
// @name 9GAG Video Controls
// @namespace https://9gag.com/
// @version 1.0.0
// @description Force native HTML5 controls on every video on 9GAG
// @match https://9gag.com/*
// @match https://*.9gag.com/*
// @run-at document-start
// @grant none
// ==/UserScript==
(function () {
'use strict';
const injectStyle = () => {
const css = `
.post-container:hover .sound-toggle,
.post-container:hover .length,
article:hover .sound-toggle,
article:hover .length {
display: none !important;
}
`;
const style = document.createElement('style');
style.textContent = css;
(document.head || document.documentElement).appendChild(style);
};
injectStyle();
const ATTR_FILTER = ['controls', 'controlslist'];
const enableControls = (video) => {
if (!(video instanceof HTMLVideoElement)) return;
if (!video.hasAttribute('controls')) {
video.setAttribute('controls', '');
}
video.removeAttribute('controlsList');
video.removeAttribute('disablepictureinpicture');
video.removeAttribute('disableremoteplayback');
};
const attrObserver = new MutationObserver((mutations) => {
for (const m of mutations) {
enableControls(m.target);
}
});
const attach = (video) => {
enableControls(video);
attrObserver.observe(video, { attributes: true, attributeFilter: ATTR_FILTER });
};
const processNode = (node) => {
if (node instanceof HTMLVideoElement) {
attach(node);
} else if (node instanceof Element) {
node.querySelectorAll('video').forEach(attach);
}
};
const treeObserver = new MutationObserver((mutations) => {
for (const m of mutations) {
m.addedNodes.forEach(processNode);
}
});
const start = () => {
document.querySelectorAll('video').forEach(attach);
treeObserver.observe(document.body, { childList: true, subtree: true });
};
if (document.body) {
start();
} else {
document.addEventListener('DOMContentLoaded', start, { once: true });
}
})();