您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Inverts video if bright flashes are detected. YouTube-native toggle + settings panel.
// ==UserScript== // @name Flashbank Protection (YouTube UI + Settings) // @namespace Violentmonkey Scripts // @match https://www.youtube.com/watch* // @grant none // @version 3.1 // @description Inverts video if bright flashes are detected. YouTube-native toggle + settings panel. // @license MIT // ==/UserScript== /*! MIT License Copyright (c) 2024 Flashbank Team Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND. */ (function () { 'use strict'; const SETTINGS_KEY = 'flashbankSettings'; const defaultSettings = { enableScript: true, showOverlayText: true, showDebugCanvas: false, enableLogging: true, thresholdPercent: 55 }; const settings = Object.assign({}, defaultSettings, JSON.parse(localStorage.getItem(SETTINGS_KEY) || '{}')); const CHECK_INTERVAL = 250; const DETECT_MODE = 'bright'; const BRIGHT_THRESHOLD = 240; const GRAY_TARGET = 80; const TOLERANCE = 10; const CANVAS_WIDTH = 320; const STRIPE_HEIGHT = 20; let debugCanvas = null; let ctx = null; const log = (...args) => { if (settings.enableLogging && settings.enableScript) console.log('[Flashbank]', ...args); }; const isBright = (r, g, b) => r >= BRIGHT_THRESHOLD && g >= BRIGHT_THRESHOLD && b >= BRIGHT_THRESHOLD; const isGrayMatch = (r, g, b) => Math.abs(r - GRAY_TARGET) < TOLERANCE && Math.abs(g - GRAY_TARGET) < TOLERANCE && Math.abs(b - GRAY_TARGET) < TOLERANCE; function saveSettings() { localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings)); } function createOverlayText() { let overlay = document.getElementById('flashbank-indicator'); if (!overlay) { overlay = document.createElement('div'); overlay.id = 'flashbank-indicator'; overlay.textContent = '👁 Bright Flash Protection Active'; Object.assign(overlay.style, { position: 'fixed', top: '12px', right: '12px', padding: '8px 14px', backgroundColor: 'rgba(0, 0, 0, 0.85)', color: '#00ff88', fontWeight: 'bold', fontSize: '14px', fontFamily: 'sans-serif', borderRadius: '6px', zIndex: 999999, pointerEvents: 'none' }); document.body.appendChild(overlay); } overlay.style.display = settings.enableScript && settings.showOverlayText ? 'block' : 'none'; } function createDebugCanvas() { if (debugCanvas) return debugCanvas; debugCanvas = document.createElement('canvas'); debugCanvas.width = CANVAS_WIDTH; debugCanvas.height = STRIPE_HEIGHT; Object.assign(debugCanvas.style, { position: 'fixed', bottom: '10px', left: '50%', transform: 'translateX(-50%)', width: `${CANVAS_WIDTH}px`, height: `${STRIPE_HEIGHT}px`, border: '2px solid red', zIndex: 999999, pointerEvents: 'none' }); document.body.appendChild(debugCanvas); ctx = debugCanvas.getContext('2d', { willReadFrequently: true }); updateDynamicStates(); return debugCanvas; } function updateDynamicStates() { const overlay = document.getElementById('flashbank-indicator'); if (overlay) overlay.style.display = settings.enableScript && settings.showOverlayText ? 'block' : 'none'; if (debugCanvas) debugCanvas.style.display = settings.enableScript && settings.showDebugCanvas ? 'block' : 'none'; } function createSettingsPanel(buttonWrapper) { const panel = document.createElement('div'); Object.assign(panel.style, { position: 'absolute', background: '#1e1e1e', color: '#fff', padding: '10px', borderRadius: '8px', boxShadow: '0 2px 10px rgba(0,0,0,0.4)', fontSize: '13px', fontFamily: 'sans-serif', zIndex: '2147483647', width: '240px', display: 'none' }); const addToggle = (label, key) => { const wrapper = document.createElement('label'); wrapper.style.display = 'block'; wrapper.style.marginBottom = '6px'; const input = document.createElement('input'); input.type = 'checkbox'; input.checked = settings[key]; input.onchange = () => { settings[key] = input.checked; saveSettings(); updateDynamicStates(); }; wrapper.appendChild(input); wrapper.appendChild(document.createTextNode(' ' + label)); panel.appendChild(wrapper); }; addToggle('Enable script', 'enableScript'); addToggle('Show overlay', 'showOverlayText'); addToggle('Show debug canvas', 'showDebugCanvas'); addToggle('Enable console logs', 'enableLogging'); const thresholdLabel = document.createElement('label'); thresholdLabel.textContent = `Threshold: ${settings.thresholdPercent}%`; thresholdLabel.style.display = 'block'; thresholdLabel.style.marginTop = '10px'; const thresholdSlider = document.createElement('input'); thresholdSlider.type = 'range'; thresholdSlider.min = 10; thresholdSlider.max = 100; thresholdSlider.value = settings.thresholdPercent; thresholdSlider.style.width = '100%'; thresholdSlider.oninput = () => { settings.thresholdPercent = parseInt(thresholdSlider.value, 10); thresholdLabel.textContent = `Threshold: ${settings.thresholdPercent}%`; saveSettings(); }; panel.appendChild(thresholdLabel); panel.appendChild(thresholdSlider); document.body.appendChild(panel); const toggleBtn = buttonWrapper.querySelector('button'); toggleBtn.addEventListener('click', (e) => { e.stopPropagation(); const rect = toggleBtn.getBoundingClientRect(); panel.style.top = `${rect.bottom + window.scrollY + 8}px`; panel.style.left = `${rect.left + window.scrollX}px`; panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; }); document.addEventListener('click', (e) => { if (!panel.contains(e.target) && !buttonWrapper.contains(e.target)) { panel.style.display = 'none'; } }); } function createSettingsButtonInYouTubeBar() { const container = document.querySelector('#top-level-buttons-computed'); if (!container || container.querySelector('[data-flashbank-btn]')) return; const wrapper = document.createElement('div'); wrapper.style.position = 'relative'; wrapper.setAttribute('data-flashbank-btn', 'true'); const button = document.createElement('button'); button.className = 'yt-spec-button-shape-next yt-spec-button-shape-next--tonal yt-spec-button-shape-next--mono yt-spec-button-shape-next--size-m yt-spec-button-shape-next--icon-leading yt-spec-button-shape-next--enable-backdrop-filter-experiment'; button.style.marginLeft = '8px'; button.setAttribute('aria-label', 'Flashbank Protection'); button.title = 'Flashbank Protection'; const icon = document.createElement('yt-icon'); icon.style.width = '24px'; icon.style.height = '24px'; const svgNS = "http://www.w3.org/2000/svg"; const svg = document.createElementNS(svgNS, "svg"); svg.setAttribute("viewBox", "0 0 24 24"); svg.setAttribute("focusable", "false"); svg.setAttribute("style", "width: 100%; height: 100%;"); const path = document.createElementNS(svgNS, "path"); path.setAttribute("fill", "currentColor"); path.setAttribute("d", "M12 2C8.13 2 5 6 5 12s3.13 10 7 10 7-4 7-10S15.87 2 12 2zm0 18c-2.76 0-5-3.58-5-8s2.24-8 5-8 5 3.58 5 8-2.24 8-5 8z"); svg.appendChild(path); icon.appendChild(svg); const text = document.createElement('div'); text.className = 'yt-spec-button-shape-next__button-text-content'; text.textContent = 'Flashbank'; button.appendChild(icon); button.appendChild(text); wrapper.appendChild(button); container.appendChild(wrapper); createSettingsPanel(wrapper); } function startFlashbank(video) { if (video.dataset.flashbankAttached === 'true') return; video.dataset.flashbankAttached = 'true'; log('Video found, starting flash protection'); createSettingsButtonInYouTubeBar(); createOverlayText(); createDebugCanvas(); setInterval(() => { if (!settings.enableScript || video.paused || video.ended) return; try { const vidW = video.videoWidth; const vidH = video.videoHeight; if (!vidW || !vidH) return; const stripeYs = [0, Math.floor(vidH / 2 - STRIPE_HEIGHT / 2), vidH - STRIPE_HEIGHT]; let triggered = false; for (const y of stripeYs) { ctx.drawImage(video, 0, y, vidW, STRIPE_HEIGHT, 0, 0, CANVAS_WIDTH, STRIPE_HEIGHT); const { data } = ctx.getImageData(0, 0, CANVAS_WIDTH, STRIPE_HEIGHT); let match = 0; const total = data.length / 4; for (let i = 0; i < data.length; i += 4) { const [r, g, b] = [data[i], data[i + 1], data[i + 2]]; if ((DETECT_MODE === 'bright' && isBright(r, g, b)) || (DETECT_MODE === 'gray' && isGrayMatch(r, g, b))) { match++; } } const percent = (match / total) * 100; log(`Stripe y=${y} match: ${percent.toFixed(2)}%`); if (percent >= settings.thresholdPercent) { triggered = true; break; } } video.style.filter = triggered ? 'invert(1)' : 'none'; } catch (err) { log('Frame read error:', err); } }, CHECK_INTERVAL); } function watchForVideo() { const tryInit = () => { const video = document.querySelector('video'); if (video) startFlashbank(video); }; tryInit(); const observer = new MutationObserver(tryInit); observer.observe(document.body, { childList: true, subtree: true }); } function monitorButtonInjection() { const observer = new MutationObserver(() => { const container = document.querySelector('#top-level-buttons-computed'); if (container && !container.querySelector('[data-flashbank-btn]')) { createSettingsButtonInYouTubeBar(); } }); observer.observe(document.body, { childList: true, subtree: true }); } watchForVideo(); monitorButtonInjection(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址