您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
ビリビリのアレをリアルタイムでやってみる
当前为
// ==UserScript== // @name Masked Watch // @namespace https://github.com/segabito/ // @description ビリビリのアレをリアルタイムでやってみる // @match *://www.nicovideo.jp/* // @match *://live.nicovideo.jp/* // @match *://anime.nicovideo.jp/* // @exclude *://ads*.nicovideo.jp/* // @exclude *://www.upload.nicovideo.jp/* // @exclude *://www.nicovideo.jp/watch/*?edit=* // @exclude *://ch.nicovideo.jp/tool/* // @exclude *://flapi.nicovideo.jp/* // @exclude *://dic.nicovideo.jp/p/* // @exclude *://ext.nicovideo.jp/thumb/* // @exclude *://ext.nicovideo.jp/thumb_channel/* // @version 0.0.2 // @grant none // @author 名無しさん // @license public domain // ==/UserScript== // chrome://flags/#enable-experimental-web-platform-features (() => { const PRODUCT = 'MaskedWatch'; const monkey = (PRODUCT) => { 'use strict'; const INTERVAL = 30; var VER = '0.0.1'; //@version const ENV = 'STABLE'; //@environment const createWorker = (func, options = {}) => { const src = `(${func.toString()})(self);`; const blob = new Blob([src], {type: 'text/javascript'}); const url = URL.createObjectURL(blob); return new Worker(url, options); }; const 業務 = function(self) { const FASTMODE = false; // false 精度重視 true 速度重視 let canvas, ctx; const init = params => { ({canvas} = params); const {width, height} = params; ctx = canvas.getContext('2d'); ctx.fillStyle = 'rgba(255, 255, 255, 0.5)'; canvas.width = width || 320; canvas.height = height || 180; }; const detector = new (self || window).FaceDetector({fastMode: FASTMODE}); const detect = async ({bitmap}) => { // console.time('detect'); const faces = await detector.detect(bitmap); // console.timeLog('detect', 'detector.detect'); ctx.beginPath(); ctx.fillRect(0, 0, canvas.width, canvas.height); for (const face of faces) { const {x, y , width, height} = face.boundingBox; ctx.clearRect(x / 2 - 10, y / 2 -16 , width / 2 + 20, height / 2 + 32); } // console.timeLog('detect', 'draw'); const dataURL = await toDataURL(canvas); // console.timeEnd('detect'); return dataURL; }; const reader = new FileReader(); const toDataURL = async (canvas, type = 'image/png') => { const blob = await canvas.convertToBlob({type}); return new Promise((ok, ng) => { reader.onload = () => { ok(reader.result); }; reader.onerror = ng; reader.readAsDataURL(blob); }); }; self.onmessage = async e => { const {command, params} = e.data.body; switch (command) { case 'init': init(params); self.postMessage({body: {command: 'init', params: {}, status: 'ok'}}); break; case 'detect': { const dataURL = await detect(params); self.postMessage({body: {command: 'data', params: {dataURL}, status: 'ok'}}); } break; } }; }; const createFaceDetector = ({video, layer, interval}) => { const worker = createWorker(業務, {name: 'Facelook'}); const width = 640, height = 360; const transferCanvas = new OffscreenCanvas(width, height); const ctx = transferCanvas.getContext('2d', {alpha: false}); const workCanvas = document.createElement('canvas'); workCanvas.width = width; workCanvas.height = height; // for debug // Object.assign(workCanvas.style, { // border: '1px solid #888', // left: 0, // bottom: 0, // position:'fixed', // zIndex:'100000', // width:`${width / 2}px`, // height:`${height / 2}px`, // opacity: 0.8, // filter: 'drop-shadow(2px 2px 2px black)', // pointerEvents: 'none', // userSelect: 'none' // }); // document.body.append(workCanvas); const offscreenCanvas = workCanvas.transferControlToOffscreen(); worker.postMessage({body: {command: 'init', params: {canvas: offscreenCanvas, width: width / 2, height: height / 2}}}, [offscreenCanvas]); let currentTime = video.currentTime; let isBusy = true; worker.addEventListener('message', e => { const {command, params} = e.data.body; switch (command) { case 'init': console.log('initialized'); isBusy = false; break; case 'data': { const url = `url('${params.dataURL}')`; layer.style.maskImage = url; layer.style.webkitMaskImage = url; isBusy = false; } break; } }); interval = interval || INTERVAL; const onTimer = () => { if (currentTime === video.currentTime || isBusy) { return; } currentTime = video.currentTime; const vw = video.videoWidth, vh = video.videoHeight; const ratio = Math.min(width / vw, height / vh); const dw = vw * ratio, dh = vh * ratio; ctx.drawImage(video, (width - dw) / 2, (height - dh) / 2, dw, dh); const bitmap = transferCanvas.transferToImageBitmap(); isBusy = true; worker.postMessage({body: {command: 'detect', params: {bitmap}}}, [bitmap]); }; let timer = setInterval(onTimer, interval); return { start: () => timer = setInterval(onTimer, interval), stop: () => timer = clearInterval(timer), }; }; const vmap = new WeakMap(); const watch = () => { [...document.querySelectorAll('video, zenza-video')] .filter(video => !video.paused && !vmap.has(video)) .forEach(video => { // 対応プレイヤー増やすならココ let layer, type = 'UNKNOWN'; if (video.closest('#MainVideoPlayer')) { layer = document.querySelector('.CommentRenderer'); type = 'NICO VIDEO'; } else if (video.closest('.zenzaPlayerContainer')) { layer = document.querySelector('.commentLayerFrame'); type = 'ZenzaWatch'; } else if (video.closest('[class*="__leo"]')) { layer = document.querySelector('#comment-layer-container canvas'); type = 'NICO LIVE'; } console.log('%ctype: "%s"', 'font-weight: bold', type); Object.assign(layer.style, { backgroundSize: 'contain', maskSize: 'contain', webkitMaskSize: 'contain', maskRepeat: 'no-repeat', webkitMaskRepeat: 'no-repeat', maskPosition: 'center center', webkitMaskPosition: 'center center' }); vmap.set(video, layer ? createFaceDetector({video: video.drawableElement || video, layer, interval: INTERVAL}) : type ); }); }; setInterval(watch, 1000); console.log('%cMasked Watch', 'font-size: 200%;', `ver ${VER}`); }; const loadGm = () => { const script = document.createElement('script'); script.id = `${PRODUCT}Loader`; script.setAttribute('type', 'text/javascript'); script.setAttribute('charset', 'UTF-8'); script.append(` (() => { (${monkey.toString()})("${PRODUCT}"); })();`); (document.head || document.documentElement).append(script); }; loadGm(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址