您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Dark mode but for videos. Remembered your options per channel. ctrl+i to show/hide options, 'i' to toggle the area
// ==UserScript== // @name Video Area Inverter // @namespace twitch.tv/simplevar // @version 2024-01-08 // @description Dark mode but for videos. Remembered your options per channel. ctrl+i to show/hide options, 'i' to toggle the area // @author SimpleVar // @match https://www.youtube.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com // @license The Unlicense // @run-at document-end // @grant none // ==/UserScript== (() => { 'use strict'; setTimeout(() => { const style = document.createElement("style"); document.head.appendChild(style); // must append before you can access sheet property style.sheet.addRule('.html5-video-container', 'height: 100% !important') style.sheet.addRule('.html5-video-player', 'overflow: visible !important; z-index: unset') style.sheet.addRule('#player-container', 'z-index: 1') style.sheet.addRule('#ytd-player', 'overflow: visible !important') style.sheet.addRule('body[data-darkverysimpleunique="x"] .darkverysimpleunique', 'opacity: 1') style.sheet.addRule('body[data-darkverysimpleuniqueui="x"] .darkverysimpleuniqueui', 'opacity: 1') style.sheet.addRule('.darkverysimpleuniqueinput', 'pointer-events: none; user-select: auto;') style.sheet.addRule('body[data-darkverysimpleuniqueui="x"] .darkverysimpleuniqueinput', 'pointer-events: auto;') style.sheet.addRule('.darkverysimpleunique', 'position: absolute; pointer-events: none; transition: opacity 200ms; opacity: 0; border: 3px solid black; backdrop-filter: invert(1); box-sizing: border-box; box-shadow: black 0 0 0 2px; border-radius: calc(var(--height) * var(--radius) * 0.1px); height: calc(var(--height) * 1%); aspect-ratio: var(--aspect); left: 50%; transform: translateX(calc(var(--left) / var(--aspect) * 1%))') style.sheet.addRule('.darkverysimpleuniqueui', 'position: absolute; pointer-events: none; user-select: none; transition: opacity 80ms; opacity: 0; bottom: 0; right: 0; transform: translateY(100%); display: flex; align-items: baseline; font-size: medium; background-color: rgba(10, 10, 10, 0.9); padding-bottom: 0.334em; color: #eee') }, 100) document.body.addEventListener('keydown', e => { const anyModifier = e.altKey | e.ctrlKey | e.shiftKey | e.metaKey if (e.key === 'i' && !anyModifier) { e.preventDefault() e.stopPropagation() const x = document.body.dataset.darkverysimpleunique === 'x' document.body.dataset.darkverysimpleunique = x ? '' : 'x' } if (e.key === 'i' && e.ctrlKey) { e.preventDefault() e.stopPropagation() const x = document.body.dataset.darkverysimpleuniqueui === 'x' document.body.dataset.darkverysimpleuniqueui = x ? '' : 'x' } }, {passive: false, capture: true}) function waitTruthy(pollInterval, fn) { return new Promise((res, _) => { poll() function poll() { const x = fn() if (x) res(x) else setTimeout(poll, pollInterval) } }) } async function waitEl(elOrSelector, predicate = undefined, pollInterval = 100, knownParent = undefined) { if (!(elOrSelector instanceof HTMLElement)) { knownParent ??= document elOrSelector = await waitTruthy(pollInterval, () => knownParent.querySelector(elOrSelector)) } if (predicate) await waitTruthy(pollInterval, () => predicate(elOrSelector)) return elOrSelector } ;(async () => { const ch = (await waitEl('.ytd-channel-name a.yt-formatted-string')).getAttribute('href') const vid = await waitEl('video') fixVideoElement(vid, ch) })(); /* new MutationObserver(muts => { for (const m of muts) { for (const el of m.addedNodes) { if (el.tagName === 'video') setTimeout(fixVideoElement, 1000, el) } } }).observe(document.body, { childList: true, subtree: true }); */ function fixVideoElement(el, ch) { if (el.__eww_9832475) return el.__eww_9832475 = true el.style.position = 'relative' const area = document.createElement('div') el.parentElement.appendChild(area) area.className = 'darkverysimpleunique' const ui = document.createElement('div') el.parentElement.parentElement.parentElement.appendChild(ui) ui.className = 'darkverysimpleuniqueui' const STOP = e => { e.stopPropagation() }; const chStorageKey = 'darkverysimpleuniqueedges_' + ch debugger let memberedEdges = null try { memberedEdges = JSON.parse(localStorage.getItem(chStorageKey) ?? '{}') } catch {} if (!(memberedEdges instanceof Object)) memberedEdges = null const edges = Object.assign({left: 0, top: 0, height: 0, aspect: 0, radius: 0}, memberedEdges ?? {}) const mkInp = (key, label, min, max) => { if (label) { const lbl = document.createElement('label') lbl.textContent = label lbl.style.marginLeft = '1ch' ui.appendChild(lbl) } const inp = document.createElement('input') ui.appendChild(inp) inp.className = 'darkverysimpleuniqueinput' inp.type = 'number' inp.min = min inp.max = max inp.step = 0.025 inp.style.width = '6ch' inp.style.fontSize = 'inherit' const storageKey = 'darkverysimpleuniqueedge' + key inp.value = (memberedEdges && memberedEdges[key]) ?? localStorage.getItem(storageKey) if (!inp.value && inp.value !== 0) inp.value = key === 'aspect' ? 1 : 25 const onChange = e => { e && STOP(e); edges[key] = inp.value if (key === 'radius') area.style.setProperty('--radius', +(inp.value ?? 25)) else if (key === 'height') area.style.setProperty('--height', +(edges.height ?? 25)) else if (key === 'aspect') area.style.setProperty('--aspect', +(inp.value ?? 100) * 0.01) else if (key === 'left') area.style.setProperty('--left', +(inp.value ?? 0)) else area.style[key] = (50 - +(inp.value ?? 0)) + '%' localStorage.setItem(storageKey, inp.value) localStorage.setItem(chStorageKey, JSON.stringify(edges)) }; onChange() inp.__onChange = onChange const stoppedEvents = [ 'click', 'dblclick', 'auxclick', 'contextmenu', 'wheel', 'scroll', 'tap', 'pointerdown', 'pointerup', 'touchstart', 'mouseleave', 'mousedown', 'panmove', 'panstart', 'panend', 'pinchin', 'pinchout', 'mouseover', 'mousemove', 'focusin', 'gesturechange', 'gestureend', 'keyup', ]; for (const ev of stoppedEvents) inp.addEventListener('click', STOP) inp.addEventListener('keydown', e => { STOP(e) let dy = 0 switch (e.key) { case 'ArrowUp': dy = 1; break; case 'ArrowDown': dy = -1; break; } if (dy) { dy *= (e.ctrlKey | e.shiftKey) ? 5 : 0.1 inp.value = dy + +(inp.value ?? 0) } }) inp.addEventListener('input', onChange) return inp }; mkInp('top', 'T=', 0, 100) mkInp('height', 'H=', 0, 100) mkInp('left', 'L=', undefined, undefined) mkInp('aspect', 'W=', 0, undefined) mkInp('radius', 'radius=', 0, 50) } })()