Filtera

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

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

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