Filtera

Filter effects for videos and more (CSS brightness, saturation, contrast etc...)

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==UserScript==
// @name		Filtera
// @namespace	https://greasyfork.org/en/scripts/510944-filtera
// @version		2024-09-30|v0.1.0-1
// @description	Filter effects for videos and more (CSS brightness, saturation, contrast etc...)
// @author		V.H.
// @copyright	V.H.
// @match		*://*/*
// @grant		unsafeWindow
// @grant		GM_log
// @grant		GM_registerMenuCommand
// @grant		GM_unregisterMenuCommand
// @grant		GM_addStyle
// @grant		GM_getValue
// @grant		GM_setValue
// @run-at		document-body
// @tag			accessibility
// @supportURL	https://greasyfork.org/en/scripts/510944-filtera/feedback
// @license		MIT
// @noframes
// ==/UserScript==

"use strict";

const	cfg				= {
			name:			"Filtera",
			prefix:			"gm_vh_fltr_",
			commands:		{
				panel:			{
					name:			"Panel",
					options:		{
						title:			"Panel",
						accessKey:		"P",
						autoClose:		true,
						id:				null,
					},
				},
			},
		},
		style		= `
			#${cfg.prefix}panel {
				--default-radius: 5px;
				--default-margin: 5px;
				--default-padding: 5px;
				--default-gap: 5px;
				
				position: fixed !important;
				z-index: 9999 !important;
				display: flex !important;
				flex-flow: column nowrap !important;
				justify-content: space-between !important;
				align-items: space-around !important;
				align-content: stretch !important;
				gap: var(--default-gap) !important;
				top: 1vh !important;
				left: 1vw !important;
				min-width: 10vw !important;
				width: 30vw !important;
				max-width: 60vw !important;
				min-height: 15vh !important;
				height: 45vh !important;
				max-height: 85vh !important;
				resize: both !important;
				overflow: auto !important;
				overscroll-behavior: contain !important;
				user-select: contain !important;
				background: radial-gradient(circle closest-corner at center, rgba(250, 250, 250, .9) 10%, rgba(100, 100, 100, .7) 90%) !important;
				border-radius: var(--default-radius) !important;
				font-family: helvetica, sans-serif !important;
				color: rgb(20, 20, 20) !important;
			}
			#${cfg.prefix}panel *[disabled] {
				cursor: not-allowed !important;
			}
			#${cfg.prefix}panel * {
				text-align: center !important;
			}
			#${cfg.prefix}panel > * {
				margin: var(--default-margin) !important;
				pointer-events: auto;
			}
			#${cfg.prefix}panel label {
				width: 30% !important;
			}
			#${cfg.prefix}panel label::after {
				content: ": " !important;
			}
			#${cfg.prefix}panel :where(h1, h2, h3) {
				margin: var(--default-margin) !important;
				text-decoration: underline wavy !important;
			}
			#${cfg.prefix}panel :where(input, select, textarea, button) {
				margin: var(--default-margin) !important;
				padding: var(--default-padding) !important;
				box-shadow: 1px 1px 1px 1px black !important;
				border-radius: var(--default-radius) !important;
				transition: all 200ms !important;
				backdrop-filter: brightness(80%) !important;
			}
			#${cfg.prefix}panel :where(input, select, textarea, button):hover {
				box-shadow: 1px 1px 2px 2px black !important;
				border-radius: calc(var(--default-radius) - 1px) !important;
				filter: brightness(80%) !important;
			}
			#${cfg.prefix}panel :where(input, select, textarea, button):active {
				box-shadow: 1px 1px 1px 1px black !important;
				border-radius: var(--default-radius) !important;
				filter: brightness(60%) !important;
			}
			#${cfg.prefix}panel :where(input, select) {
				width: 50% !important;
			}
			#${cfg.prefix}panel :where(button, input[type="checkbox"]) {
				cursor: pointer !important;
			}
			
			#${cfg.prefix}panel_close {
				position: sticky !important;
				top: 1px !important;
				z-index: 10000 !important;
				opacity: .9 !important;
			}
		`,
		panelform	= `
			<dialog id="${cfg.prefix}panel" open>
				<h3>Filtera</h3>
				<button id="${cfg.prefix}panel_close">Close</button>
				<span title="Brightness Filter (percent)">
					<label for="${cfg.prefix}panel_brightness">Brightness</label>
					<input id="${cfg.prefix}panel_brightness" name="brightness" type="number" placeholder="100%" value="100" min="0" step="1" size="10" />
				</span>
				<span title="Contrast Filter (percent)">
					<label for="${cfg.prefix}panel_contrast">Contrast</label>
					<input id="${cfg.prefix}panel_contrast" name="contrast" type="number" placeholder="100%" value="100" min="0" step="1" size="10" />
				</span>
				<span title="Saturation Filter (percent)">
					<label for="${cfg.prefix}panel_saturation">Saturation</label>
					<input id="${cfg.prefix}panel_saturation" name="saturation" type="number" placeholder="100%" value="100" step="1" size="10" />
				</span>
				<span title="Invert Filter (percent)">
					<label for="${cfg.prefix}panel_invert">Invert</label>
					<input id="${cfg.prefix}panel_invert" name="invert" type="number" placeholder="0%" value="0" min="0" max="100" step="1" size="10" />
				</span>
				<span title="Hue-Rotate Filter (degrees)">
					<label for="${cfg.prefix}panel_hue">Hue-Rotate</label>
					<input id="${cfg.prefix}panel_hue" name="hue" type="number" placeholder="0deg" value="0" min="0" max="360" step="1" size="10" />
				</span>
				<span title="Blur Filter (pixels)">
					<label for="${cfg.prefix}panel_blur">Blur</label>
					<input id="${cfg.prefix}panel_blur" name="blur" type="number" placeholder="0px" value="0" step="1" size="10" />
				</span>
				<span title="Opacity Filter (percent)">
					<label for="${cfg.prefix}panel_opacity">Opacity</label>
					<input id="${cfg.prefix}panel_opacity" name="opacity" type="number" placeholder="100%" value="100" min="0" max="100" step="1" size="10" />
				</span>
				<span title="Grayscale Filter (percent)">
					<label for="${cfg.prefix}panel_grayscale">Grayscale</label>
					<input id="${cfg.prefix}panel_grayscale" name="grayscale" type="number" placeholder="0%" value="0" min="0" max="0" step="1" size="10" />
				</span>
				<span title="Sepia Filter (percent)">
					<label for="${cfg.prefix}panel_sepia">Sepia</label>
					<input id="${cfg.prefix}panel_sepia" name="sepia" type="number" placeholder="0%" value="0" min="0" max="100" step="1" size="10" />
				</span>
				<span title="CSS Rule (video, img, svg, ...)">
					<label for="${cfg.prefix}panel_rule">Rule</label>
					<input id="${cfg.prefix}panel_rule" name="rule" type="text" placeholder="video" value="video" size="30" />
				</span>
				<button id="${cfg.prefix}panel_reset">Reset</button>
			</dialog>
		`;
let		css			= null,
		filter		= null,
		data		= {
			rule:		"video",
			brightness:	100,
			contrast:	100,
			saturation:	100,
			invert:		0,
			hue:		0,
			blur:		0,
			opacity:	100,
			grayscale:	0,
			sepia:		0,
		};

function start() {
	data							= Object.assign(data, GM_getValue(`${cfg.prefix}settings`, data));
	
	cfg.commands.panel.options.id	= GM_registerMenuCommand(cfg.commands.panel.name, panel, cfg.commands.panel.options);
	css								= GM_addStyle(style);
	
	update();
	
	GM_log(`--- '${cfg.name}' started ---`);
} //start
function stop() {
	GM_unregisterMenuCommand(cfg.commands.panel.options.id);
} //stop

function panel() {
	const	doc			= Document.parseHTMLUnsafe(panelform),
			form		= doc.getElementById(`${cfg.prefix}panel`),
			close		= doc.getElementById(`${cfg.prefix}panel_close`),
			brightness	= doc.getElementById(`${cfg.prefix}panel_brightness`),
			contrast	= doc.getElementById(`${cfg.prefix}panel_contrast`),
			saturation	= doc.getElementById(`${cfg.prefix}panel_saturation`),
			invert		= doc.getElementById(`${cfg.prefix}panel_invert`),
			hue			= doc.getElementById(`${cfg.prefix}panel_hue`),
			blur		= doc.getElementById(`${cfg.prefix}panel_blur`),
			opacity		= doc.getElementById(`${cfg.prefix}panel_opacity`),
			grayscale	= doc.getElementById(`${cfg.prefix}panel_grayscale`),
			sepia		= doc.getElementById(`${cfg.prefix}panel_sepia`),
			rule		= doc.getElementById(`${cfg.prefix}panel_rule`),
			reset		= doc.getElementById(`${cfg.prefix}panel_reset`);
	
	close.onclick		= () => form.remove();
	reset.onclick		= () => {
		data		= {
			rule:		"video",
			brightness:	100,
			contrast:	100,
			saturation:	100,
			invert:		0,
			hue:		0,
			blur:		0,
			opacity:	100,
			grayscale:	0,
			sepia:		0,
		};
		update();
	};
	brightness.value	= data.brightness;
	brightness.onchange	= () => {
		data.brightness	= Math.round(brightness.value);
		update();
	};
	contrast.value		= data.contrast;
	contrast.onchange	= () => {
		data.contrast	= Math.round(contrast.value);
		update();
	};
	saturation.value	= data.saturation;
	saturation.onchange	= () => {
		data.saturation	= Math.round(saturation.value);
		update();
	};
	invert.value		= data.invert;
	invert.onchange		= () => {
		data.invert		= Math.round(invert.value);
		update();
	};
	hue.value			= data.hue;
	hue.onchange		= () => {
		data.hue		= Math.round(hue.value);
		update();
	};
	blur.value			= data.blur;
	blur.onchange		= () => {
		data.blur		= Math.round(blur.value);
		update();
	};
	opacity.value		= data.opacity;
	opacity.onchange	= () => {
		data.opacity	= Math.round(opacity.value);
		update();
	};
	grayscale.value		= data.grayscale;
	grayscale.onchange	= () => {
		data.grayscale	= Math.round(grayscale.value);
		update();
	};
	sepia.value			= data.sepia;
	sepia.onchange		= () => {
		data.sepia		= Math.round(sepia.value);
		update();
	};
	rule.onchange		= () => {
		data.rule		= rule.value.trim();
		update();
	};
	
	document.body.appendChild(document.adoptNode(form));
	form.show();
} //panel

start();

function update() {
	GM_setValue(`${cfg.prefix}settings`, data);
	
	if (filter)	filter.remove();
	
	filter		= GM_addStyle(`
		${data.rule} {
			filter: brightness(${data.brightness}%) contrast(${data.contrast}%) saturate(${data.saturation}%) invert(${data.invert}%) hue-rotate(${data.hue}deg) blur(${data.blur}px) opacity(${data.opacity}%) grayscale(${data.grayscale}%) sepia(${data.sepia}%) !important;
		}
	`);
} //update