您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Ehance Youtube video player with filters (see "FX" button near "Fullscreen")
// ==UserScript== // @name Youtube Video Filters // @author Xavier Bouhours // @version 2024-05-25 // @description Ehance Youtube video player with filters (see "FX" button near "Fullscreen") // @description:fr Ajout de filtres vidéos au player. La propriété css filter est exploitée pour le rendu. // @description:en Luminosity and contrast for Youtube site player. // @run-at document-end // @compatible firefox // @license MIT // @homepage https://greasyfork.org/fr/scripts/495951-youtube-video-filters // @match https://*.youtube.com/* // @namespace https://greasyfork.org/users/1306337 // ==/UserScript== (function(W,D,undefined) { const V = W.videoFx = { // On récupère le player html5 de Youtube video : D.querySelector("video.html5-main-video"), controls: D.querySelector(".ytp-right-controls"), // On crée le bouton d'appel de la fenêtre des filtres fx : D.createElement("button"), // On crée le panneau de manipulation des filtres panel : D.createElement("div"), header : D.createElement("header"), // On crée le set de boutons close : D.createElement("button"), fxswitch: D.createElement("input"), // Filters store : On initialise les filtres filters : { brightness : { min:0, val:100, max:200, unit:"%", step: 1 }, contrast : { min:0, val:100, max:200, unit:"%", step: 1 }, saturate : { min:0, val:100, max:300, unit:"%", step: 1 }, grayscale : { min:0, val:0, max:100, unit:"%", step: 1 }, 'hue-rotate': { min:-360, val:0, max:360, unit:"deg", step: 1 }, invert : { min:0, val:0, max:100, unit:"%", step: 1 }, sepia : { min:0, val:0, max:100, unit:"%", step: 1 }, blur : { min:0, val:0, max:10, unit:"px", step: 1 } }, // Application des filtres à la vidéo refresh : function() { var k, c = ""; // On compile chaque filtre pour la propriété css for( k in V.filters ) { var f = V.filters[k]; c+= k+'('+ f.val + f.unit +') '; }; // On applique le style à la vidéo V.video.style.filter = c ; // console.log(c); }, // Ajouter un réglage de filtre au panneau // @see: https://blog.hubspot.com/website/html-slider // @ex: <input type='range' min='0' value='1' max='3' name='contrast'> addfilter : function( name, min, val, max ) { let C = D.createElement("div"), // Conteneur I = D.createElement("input"), // Slider L = D.createElement("label"); // Titre I.type = "range"; I.name = I.title = I.style.content = name; I.min = min; I.value = val; I.max = max; // On ajoute le conteneur pour le label et le curseur V.panel.append(C); // On ajoute le titre C.append(L); L.append(name); //… et le curseur de réglage C.append(I); I.addEventListener("change", function() { // On sauvegarde la valeur // console.log(this,I); V.filters[I.name].val = I.value ; // On applique les modifications V.refresh(); }); }, initialized : false, // Initialisation du module init : function() { if(!!V.initialized) return true ; // On ajoute le bouton "FX" aux contrôles du player V.controls.append(V.fx); V.fx.classList.add("ytp-button"); V.fx.classList.add("video-fx"); V.fx.append("FX"); V.fx.addEventListener("click", () => { V.panel.classList.remove(V.panel.classList.item(1)); // item(0) = "video-filters" ; item(1) = "hidden" }); // On ajoute le panneau des filtres… // V.controls.append(V.panel); D.querySelector("body").append(V.panel); V.panel.classList.add("video-filters"); V.panel.classList.add("hidden"); // … avec son header (draggable zone) V.panel.append(V.header); V.header.append("Video Filters"); // … avec ses filtres, var n ; for( n in V.filters ) { var f = V.filters[n]; V.addfilter( n, f.min, f.val, f.max, f.step ); }; // … et son bouton de fermeture V.header.append(V.close); V.close.append("X"); V.close.setAttribute("title","Close"); V.close.classList.add("close"); V.close.addEventListener("click", () => { V.panel.classList.add("hidden"); }); // … et le bouton switch dpour la désactivation des filtres V.header.append(V.fxswitch); V.fxswitch.classList.add("switch"); V.fxswitch.setAttribute("type","checkbox"); V.fxswitch.setAttribute("checked","true"); V.fxswitch.addEventListener("click", () => { // set On if( V.fxswitch.checked == 1 ) { V.refresh(); V.panel.setAttribute("class","video-filters"); // restore } // set Of else { V.video.style.filter = "none"; V.panel.classList.add("of"); } }); // On installe le theme V.panel.append( V.mkstyles() ); // … et on ajoute la fonctionnalité de déplacement V.draggable(V.panel); V.initialized = true ; }, // UI theme theme : ` /* Couleurs du theme en accord avec navigateur */ :root { --vf-back-r: 255; --vf-back-v: 255; --vf-back-b: 255; --vf-back-c: rgb( var(--vf-back-r), var(--vf-back-v), var(--vf-back-b) ); --vf-txt-c: black; --vf-active-c: #3e94f6; /* ff def value */ } @media (prefers-color-scheme: dark) { :root { --vf-back-r: 0; --vf-back-v: 0; --vf-back-b: 0; --vf-txt-c: white } } /* On positionne le bouton "FX" */ button.video-fx { transform: translateY(-1.5em) } /* On optimise le panneau des filtres */ .video-filters { position: absolute; z-index: 1000; width: clamp(30em,30vw,100%); background: rgba(var(--vf-back-r), var(--vf-back-v), var(--vf-back-b), .4 ); backdrop-filter: blur(.3em) brightness(50%); border-radius: 1em; display: flex; justify-content: space-evenly; flex-wrap: wrap; top: 40vh; left: 10vw; font-weight: bold; margin-bottom: 1em; overflow: hidden; box-shadow: 0 0 .4em var(--vf-txt-c) } .video-filters.hidden { display:none } /* On ajoute la zone de drag */ .video-filters header { text-align: center; padding: 1em; cursor: move; z-index: +1; background: var(--vf-back-c); color: var(--vf-txt-c); width:100% } /* On positionne les conteneurs pour chaque filtre */ .video-filters > div { box-sizing: border-box; margin-bottom: 1em; max-width: 20em; transition: all .5s ease-out } .video-filters.of > div { user-select: none; opacity: .4; filter: grayscale(100%) blur(1px); pointer-events:none; } /* On positionne les filtres */ .video-filters label, .video-filters input { display: block; width: 100% } /* On règle les intitulés des filtres */ .video-filters label { transform: translate(1em,0); color: white; text-transform: capitalize } /* Boutons, etc */ .video-filters .close, input[type='checkbox'].switch { border-radius: 1em; width:1em; height: 1em; border: 2px solid var(--vf-txt-c) } /* On positionne le bouton de fermeture */ .video-filters .close { display: block ; position: absolute ; top: .7em ; right: .7em; line-height: 0; color:transparent; font-size: 1.3em; } /* On crée le bouton switch pour les filtres */ input[type='checkbox'].switch { display: inline-block; appearance:none; text-align: center; font-size: 2em; background: #777; transform: translate(0em,.2em) ; box-shadow: 0 0 0 var(--vf-txt-c), .3em 0 0 var(--vf-txt-c), .6em 0 0 var(--vf-txt-c); } input[type='checkbox'].switch:checked { transform: translate(1em,.2em) ; box-shadow: 0 0 0 var(--vf-txt-c), -.3em 0 0 var(--vf-txt-c), -.6em 0 0 var(--vf-txt-c); background: var(--vf-active-c) } `, // Constructeur pour la feuille de styles mkstyles : function() { const s = D.createElement('style'); s.type = 'text/css'; s.id = 'video-fx-theme'; s.innerHTML = V.theme; return s; // D.getElementsByTagName('head')[0].appendChild(s); }, // Make element draggable // @see https://stackoverflow.com/questions/65022204/make-a-popup-modal-dialog-movable-draggable-once-it-has-appeared draggable: function(el) { var p1 = p2 = p3 = p4 = 0; // el.querySelector("header") V.header.onmousedown = startDrag; function startDrag(e) { e = e || W.event; e.preventDefault(); // Get the mouse cursor position at startup p3 = e.clientX; p4 = e.clientY; D.onmouseup = stopDrag; // Call a function whenever the cursor moves D.onmousemove = drag; } function drag(e) { e = e || W.event; e.preventDefault(); // Calculate the new cursor position p1 = p3 - e.clientX; p2 = p4 - e.clientY; p3 = e.clientX; p4 = e.clientY; // Set the element's new position: el.style.top = (el.offsetTop - p2) + "px"; el.style.left = (el.offsetLeft - p1) + "px"; } function stopDrag() { // Stop moving when mouse button is released D.onmouseup = D.onmousemove = null; } } } })( window, document ); // Appliquer // @see: https://addons.mozilla.org/fr/firefox/addon/greasemonkey/ if(document.location.host==="www.youtube.com") videoFx.init(); // @todo: extend to embed and other player