一元网络论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 103|回复: 0

学生必备油猴脚本:自动定位、虚拟摄像头,轻松应对网课!

[复制链接]

3万

主题

3万

帖子

9万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
96134
发表于 2024-10-2 21:56:17 | 显示全部楼层 |阅读模式
```javascript
// @name  Enhanced Virtual Camera Hook
// @namespace  updownu
// @version  1.0
// @description  Hook various camera APIs
// @match  *:/*
// @match  *:/*
// @match  *:/*
// @match  *:/*
// @match  *:/*
// @grant  unsafeWindow
// @grant  GM_xmlhttpRequest
// @run-at  document-start
// ==/UserScript==
(function() {
'use strict';
let base64Image;
// ---- Utility Class ----
class Utils {
  static generateRandomBase64(length) {
    const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    let result = '';
    for (let i = 0; i ${styles}
      
        
        Load
      
    `;
    container.appendChild(template.content.cloneNode(true));
    const shadow = container.querySelector('#stealth-container').attachShadow({ mode: 'closed' });
    shadow.appendChild(template.content.cloneNode(true));
    const stealthContainer = shadow.querySelector('#stealth-container');
    const fileInput = shadow.querySelector('#stealth-fileInput');
    const loadButton = shadow.querySelector('#stealth-loadButton');
    loadButton.onclick = () => {
      fileInput.click();
    };
    fileInput.onchange = () => {
      const reader = new FileReader();
      reader.onload = (e) => {
        base64Image = e.target.result;
        loadButton.classList.add('done');
        loadButton.textContent = '...';
      };
      reader.readAsDataURL(fileInput.files[0]);
    };
  }
}
// ---- Virtual Stream Class ----
class VirtualStream {
  constructor(width = 1280, height = 720, fps = 30) {
    this.width = width;
    this.height = height;
    this.fps = fps;
  }
  async createNoiseLayer(opacity = 0.26149721) {
    const canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;
    const ctx = canvas.getContext('2d');
    const imageData = ctx.createImageData(this.width, this.height);
    const data = imageData.data;
    for (let i = 0; i  canvasRatio) {
      sHeight = img.height;
      sWidth = sHeight * canvasRatio;
      sy = 0;
      sx = (img.width - sWidth) / 2;
    } else {
      sWidth = img.width;
      sHeight = sWidth / canvasRatio;
      sx = 0;
      sy = (img.height - sHeight) / 2;
    }
    ctx.drawImage(img, sx, sy, sWidth, sHeight, x, y, w, h);
  }
  async createDynamicFilteredStream() {
    const canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;
    const ctx = canvas.getContext('2d');
    const img = new Image();
    img.src = await Utils.getBase64Image(new StealthUI().defaultBase64Image);
    const offscreenCanvas = document.createElement('canvas');
    offscreenCanvas.width = this.width;
    offscreenCanvas.height = this.height;
    const offscreenCtx = offscreenCanvas.getContext('2d');
    img.onload = () => {
      this.drawImageCovered(offscreenCtx, img, 0, 0, offscreenCanvas.width, offscreenCanvas.height);
    };
    let noiseLayer = await this.createNoiseLayer();
    let hue = 0;
    let saturation = 100;
    let brightness = 100;
    function generateFrame() {
      hue = (hue + 0.1) % 2;
      saturation = 99 + Math.sin(Date.now() / 1000) * Math.random();
      brightness = 98 + Math.random() * 2;
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(offscreenCanvas, 0, 0);
      ctx.filter = `hue-rotate(${hue}deg) saturate(${saturation}%) brightness(${brightness}%)`;
      ctx.drawImage(canvas, 0, 0);
      ctx.filter = 'none';
      ctx.globalCompositeOperation = 'overlay';
      ctx.drawImage(noiseLayer, 0, 0);
      ctx.globalCompositeOperation = 'source-over';
    }
    const stream = canvas.captureStream(this.fps);
    set Interval(generateFrame, 1000 / this.fps);
    set Interval(() => {
      noiseLayer = this.createNoiseLayer();
    }, 100 + Math.sin(Date.now() / 1000) * 150);
    return stream;
  }
  async createWebGLFilteredStream() {
    const canvas = document.createElement('canvas');
    canvas.width = this.width;
    canvas.height = this.height;
    const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
    if (!gl) {
      console.error('WebGL not supported, falling back to 2D canvas');
      return this.createDynamicFilteredStream();
    }
    const vertexShaderSource = `
      attribute vec2 a_position;
      attribute vec2 a_texCoord;
      varying vec2 v_texCoord;
      void main() {
        gl_Position = vec4(a_position, 0, 1);
        v_texCoord = a_texCoord;
      }
    `;
    const fragmentShaderSource = `
      precision mediump float;
      uniform sampler2D u_image;
      uniform sampler2D u_noise;
      uniform float u_time;
      uniform vec2 u_noiseOffset;
      varying vec2 v_texCoord;
      vec3 rgb2hsv(vec3 c) {
        vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
        vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
        vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.w, c.r));
        float d = q.x - min(q.w, q.y);
        float e = 1.0e-10;
        return vec3(abs(q.z - q.y) / (6.0 * d + e), d / (q.x + e), q.x);
      }
      vec3 hsv2rgb(vec3 c) {
        vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
        vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
        return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
      }
      void main() {
        vec4 color = texture2D(u_image, v_texCoord);
        vec4 noise = texture2D(u_noise, v_texCoord * 4.0 + u_noiseOffset);
        vec3 hsv = rgb2hsv(color.rgb);
        float saturationAdjustment = 0.99 + sin(u_time * 0.002) * 0.01;
        hsv.y *= saturationAdjustment;
        float brightnessAdjustment = 0.98 + 0.02 * fract(sin(dot(v_texCoord, vec2(12.9898, 78.233)) * 43758.5453));
        hsv.z *= brightnessAdjustment;
        vec3 rgb = hsv2rgb(hsv);
        gl_FragColor = vec4(rgb + noise.rgb * 0.1, color.a);
      }
    `;
    function createShader(gl, type, source) {
      const shader = gl.createShader(type);
      gl.shaderSource(shader, source);
      gl.compileShader(shader);
      if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
        console.error('An error occurred compiling the shaders: ', gl.getShaderInfoLog(shader));
        gl.deleteShader(shader);
        return null;
      }
      return shader;
    }
    const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource);
    const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource);
    const program = gl.createProgram();
    gl.attachShader(program, vertexShader);
    gl.attachShader(program, fragmentShader);
    gl.linkProgram(program);
    if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
      console.error('Unable to initialize the shader program: ', gl.getProgramInfoLog(program));
      return null;
    }
    const positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      -1, -1,
       1, -1,
      -1,  1,
       1,  1,
    ]), gl.STATIC_DRAW);
    const texCoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texCoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      0, 0,
      1, 0,
      0, 1,
      1, 1,
    ]), gl.STATIC_DRAW);
    const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
    const texCoordAttributeLocation = gl.getAttribLocation(program, 'a_texCoord');
    const u_image = gl.getUniformLocation(program, 'u_image');
    const u_noise = gl.getUniformLocation(program, 'u_noise');
    const u_time = gl.getUniformLocation(program, 'u_time');
    const u_noiseOffset = gl.getUniformLocation(program, 'u_noiseOffset');
    gl.useProgram(program);
    gl.enableVertexAttribArray(positionAttributeLocation);
    gl.enableVertexAttribArray(texCoordAttributeLocation);
    const imgTexture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, imgTexture);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    const img = new Image();
    img.src = await Utils.getBase64Image(new StealthUI().defaultBase64Image);
    img.onload = () => {
      gl.bindTexture(gl.TEXTURE_2D, imgTexture);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
    };
    function createNoiseTexture(gl, size) {
      const pixels = new Uint8Array(size * size * 4);
      for (let i = 0; i = 1000 / this.fps) {
        render(currentTime);
        lastTime = currentTime;
      }
      requestAnimationFrame(update);
    }
    update(0);
    return stream;
  }
  async createVirtualStream() {
    return await this.createWebGLFilteredStream();
  }
}
// ---- API Hook Class ----
class APIHook {
  constructor() {
    this.methodsLookupTable = new WeakMap();
    this.virtualDeviceId = Utils.generateRandomBase64(43) + '=';
    this.virtualGroupId = Utils.generateRandomBase64(43) + '=';
    this.hookMediaDevices();
    this.hookRTCPeerConnection();
    this.hookMediaStreamTrack();
    this.hookImageCapture();
    this.hookGeolocation();
    this.Queueing();
  }
  Queueing() {
    const originalToStr = Function.prototype.toString;
    const map = this.methodsLookupTable;
    Function.prototype.toString = new Proxy(originalToStr, {
      apply(target, thisArg, argumentsList) {
        if (map.has(thisArg)) {
          return map.get(thisArg).toString();
        }
        return Reflect.apply(target, thisArg, argumentsList);
      }
    });
  }
  // Helper function to replace a method with a hook
  replaceMethod(obj, methodName, newMethod) {
    const oldMethod = obj[methodName];
    obj[methodName] = newMethod;
    this.methodsLookupTable.set(newMethod, oldMethod);
  }
  // Hook navigator.mediaDevices methods
  hookMediaDevices() {
    this.replaceMethod(navigator.mediaDevices, 'enumerateDevices', async () => {
      const devices = await this.methodsLookupTable.get(navigator.mediaDevices.enumerateDevices).call(navigator.mediaDevices);
      const virtualDevice = new MediaDeviceInfo({
        deviceId: this.virtualDeviceId,
        groupId: this.virtualGroupId,
        kind: 'videoinput',
        label: 'Integrated Webcam'
      });
      return [virtualDevice, ...devices];
    });
    this.replaceMethod(navigator.mediaDevices, 'getUserMedia', async (constraints) => {
      if (constraints && constraints.video) {
        return await new VirtualStream().createVirtualStream();
      }
      return this.methodsLookupTable.get(navigator.mediaDevices.getUserMedia).call(navigator.mediaDevices, constraints);
    });
    this.replaceMethod(navigator.mediaDevices, 'getDisplayMedia', async (constraints) => {
      return await new VirtualStream().createVirtualStream();
    });
  }
  // Hook RTCPeerConnection
  hookRTCPeerConnection() {
    this.replaceMethod(window, 'RTCPeerConnection', (originalRTCPeerConnection) => {
      return class extends originalRTCPeerConnection {
        addTrack(track, ...args) {
          if (track.kind === 'video' && track.id === this.virtualDeviceId) {
            const stream = new VirtualStream().createVirtualStream();
            const [videoTrack] = stream.getVideoTracks();
            return originalRTCPeerConnection.prototype.addTrack.call(this, videoTrack, ...args.slice(1));
          }
          return originalRTCPeerConnection.prototype.addTrack.call(this, track, ...args);
        }
        getSetting() {
          const settings = originalRTCPeerConnection.prototype.getSetting.call(this);
          if (settings.deviceId === this.virtualDeviceId) {
            return {
              ...settings,
              deviceId: this.virtualDeviceId,
              groupId: this.virtualGroupId,
              width: 1280,
              height: 720,
              aspectRatio: 16 / 9,
              frameRate: 30,
              facingMode: 'user',
              resizeMode: 'none'
            };
          }
          return settings;
        }
      };
    });
  }
  // Hook MediaStreamTrack
  hookMediaStreamTrack() {
    this.replaceMethod(window, 'MediaStreamTrack', (originalMediaStreamTrack) => {
      return class extends originalMediaStreamTrack {
        getSetting() {
          const settings = originalMediaStreamTrack.prototype.getSetting.call(this);
          if (settings.deviceId === this.virtualDeviceId) {
            return {
              ...settings,
              deviceId: this.virtualDeviceId,
              groupId: this.virtualGroupId,
              width: 1280,
              height: 720,
              aspectRatio: 16 / 9,
              frameRate: 30,
              facingMode: 'user',
              resizeMode: 'none'
            };
          }
          return settings;
        }
        applyConstraints(newConstraints) {
          Object.assign(this.constraints, newConstraints);
          return Promise.resolve();
        }
        stop() {
          this.enabled = false;
          this.originalTrack.stop();
        }
      };
    });
  }
  // Hook ImageCapture
  hookImageCapture() {
    this.replaceMethod(window, 'ImageCapture', (originalImageCapture) => {
      return class extends originalImageCapture {
        constructor(track) {
          if (track.kind === 'video' && track.id === this.virtualDeviceId) {
            const virtualTrack = new VirtualStreamTrack(track);
            super(virtualTrack);
          } else {
            super(track);
          }
        }
      };
    });
  }
  // Geolocation Spoofing
  async getCoordinatesFromAddress(address) {
    return new Promise((resolve, reject) => {
      GM_xmlhttpRequest({
        method: 'GET',
        url: `https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(address)}&format=json`,
        onload: (response) => {
          if (response.status === 200) {
            const data = JSON.parse(response.responseTex
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|一元网络论坛

GMT+8, 2024-11-26 07:36 , Processed in 0.079624 second(s), 20 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表