Filtera

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

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==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