VideoControlReset

视频增强插件:手动增强、跨域安全、Lucide图标、美化控制条、音量调节、进度条、iframe支持、键盘快捷键等。

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         VideoControlReset
// @namespace    https://greasyfork.org/
// @version      1.0
// @description  视频增强插件:手动增强、跨域安全、Lucide图标、美化控制条、音量调节、进度条、iframe支持、键盘快捷键等。
// @author       DM
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  console.log("[🎬 Video Enhancer] 脚本加载成功 ✅");

  /*** 引入 Lucide 图标库 ***/
  const ICON_URL = "https://unpkg.com/lucide@latest/dist/umd/lucide.js";
  if (!window.lucide) {
    const script = document.createElement("script");
    script.src = ICON_URL;
    script.onload = () => window.lucide.createIcons();
    document.head.appendChild(script);
  }

  /** 样式 **/
  const style = document.createElement("style");
  style.textContent = `
    .ve-enhance-btn-box {
      position: absolute;
      top: 8px;
      right: 8px;
      display: flex;
      gap: 6px;
      z-index: 999999;
    }
    .ve-btn {
      border: none;
      background: rgba(0,0,0,0.5);
      color: #fff;
      border-radius: 8px;
      cursor: pointer;
      padding: 6px;
      display: flex;
      align-items: center;
      transition: all .2s ease;
    }
    .ve-btn:hover {
      background: rgba(0,0,0,0.8);
      transform: scale(1.15);
    }
    .ve-bar {
      position: absolute;
      left: 50%;
      bottom: 12px;
      transform: translateX(-50%);
      display: flex;
      align-items: center;
      gap: 10px;
      background: rgba(0,0,0,0.45);
      padding: 8px 14px;
      border-radius: 20px;
      backdrop-filter: blur(6px);
      opacity: 0;
      transition: opacity 0.3s ease;
      z-index: 999999;
    }
    .video-wrapper:hover .ve-bar {
      opacity: 1;
    }
    .ve-progress {
      width: 160px;
      height: 5px;
      background: rgba(255,255,255,0.3);
      border-radius: 3px;
      cursor: pointer;
      position: relative;
      overflow: hidden;
    }
    .ve-progress-inner {
      height: 100%;
      width: 0%;
      background: linear-gradient(90deg,#4fc3f7,#81c784,#f06292);
      border-radius: 3px;
      transition: width 0.1s linear;
    }
    .ve-volume { width: 80px; }
  `;
  document.head.appendChild(style);

  /** 工具函数 **/
  const createIcon = (name, size = 20) => {
    const i = document.createElement("i");
    i.dataset.lucide = name;
    i.style.width = `${size}px`;
    i.style.height = `${size}px`;
    return i;
  };

  const createButton = (icon, title, onClick) => {
    const btn = document.createElement("button");
    btn.className = "ve-btn";
    btn.title = title;
    btn.appendChild(createIcon(icon));
    btn.onclick = onClick;
    return btn;
  };

  /** 截图功能 **/
  function captureFrame(video) {
    const canvas = document.createElement("canvas");
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    canvas.getContext("2d").drawImage(video, 0, 0);
    const link = document.createElement("a");
    link.href = canvas.toDataURL("image/png");
    link.download = "screenshot.png";
    link.click();
  }

  /** 增强核心函数 **/
  function enhanceVideo(video) {
    if (video.dataset.enhanced) return;
    video.dataset.enhanced = "true";
    const wrapper = video.parentElement;
    wrapper.classList.add("video-wrapper");

    const bar = document.createElement("div");
    bar.className = "ve-bar";

    const playBtn = createButton("play", "播放/暂停", () => {
      video.paused ? video.play() : video.pause();
    });

    const backBtn = createButton("rewind", "后退5秒", () => video.currentTime -= 5);
    const forwardBtn = createButton("fast-forward", "前进5秒", () => video.currentTime += 5);

    const speedBtn = createButton("gauge", "倍速切换", () => {
      const speeds = [1, 1.25, 1.5, 2];
      const next = speeds[(speeds.indexOf(video.playbackRate) + 1) % speeds.length];
      video.playbackRate = next;
      speedBtn.title = `倍速:${next}x`;
    });

    const pipBtn = createButton("monitor-up", "画中画", () => video.requestPictureInPicture());
    const fsBtn = createButton("maximize", "全屏", () => video.requestFullscreen());
    const shotBtn = createButton("camera", "截图", () => captureFrame(video));

    // 进度条
    const progress = document.createElement("div");
    progress.className = "ve-progress";
    const inner = document.createElement("div");
    inner.className = "ve-progress-inner";
    progress.appendChild(inner);
    progress.onclick = (e) => {
      const rect = progress.getBoundingClientRect();
      const percent = (e.clientX - rect.left) / rect.width;
      video.currentTime = percent * video.duration;
    };
    video.addEventListener("timeupdate", () => {
      inner.style.width = `${(video.currentTime / video.duration) * 100}%`;
    });

    // 音量
    const volBox = document.createElement("div");
    volBox.style.display = "flex";
    volBox.style.alignItems = "center";
    const volIcon = createIcon("volume-2");
    const volSlider = document.createElement("input");
    volSlider.type = "range";
    volSlider.className = "ve-volume";
    volSlider.min = 0;
    volSlider.max = 1;
    volSlider.step = 0.05;
    volSlider.value = video.volume;
    volSlider.oninput = () => {
      video.volume = volSlider.value;
      volIcon.dataset.lucide = video.volume == 0 ? "volume-x" : "volume-2";
      window.lucide && window.lucide.createIcons();
    };
    volBox.append(volIcon, volSlider);

    [playBtn, backBtn, forwardBtn, speedBtn, pipBtn, fsBtn, shotBtn, progress, volBox].forEach(b => bar.appendChild(b));
    wrapper.appendChild(bar);

    // 播放状态联动
    const updatePlayIcon = () => {
      playBtn.firstChild.dataset.lucide = video.paused ? "play" : "pause";
      window.lucide && window.lucide.createIcons();
    };
    video.addEventListener("play", updatePlayIcon);
    video.addEventListener("pause", updatePlayIcon);

    window.lucide && window.lucide.createIcons();
  }

  /** 添加增强/下载按钮 **/
  function addEnhanceButton(video) {
    if (video.dataset.hasBtn) return;
    video.dataset.hasBtn = "true";

    const box = document.createElement("div");
    box.className = "ve-enhance-btn-box";

    const enhance = createButton("sparkles", "增强视频", () => enhanceVideo(video));
    const download = createButton("download", "下载视频", () => {
      const a = document.createElement("a");
      a.href = video.src;
      a.download = "video.mp4";
      a.click();
    });

    box.append(enhance, download);
    video.parentElement.style.position = "relative";
    video.parentElement.appendChild(box);
    window.lucide && window.lucide.createIcons();
  }

  /** 扫描所有视频(支持 iframe) **/
  function scanVideos(root = document) {
    root.querySelectorAll("video").forEach(v => addEnhanceButton(v));

    root.querySelectorAll("iframe").forEach(frame => {
      try {
        const doc = frame.contentDocument || frame.contentWindow.document;
        if (!doc) return;
        const vids = doc.querySelectorAll("video");
        vids.forEach(v => addEnhanceButton(v));
      } catch {
        // 跨域 iframe 忽略
      }
    });
  }

  scanVideos();

  /** 监听增量变化 **/
  const obs = new MutationObserver(mutations => {
    for (const m of mutations)
      m.addedNodes.forEach(n => n.nodeType === 1 && scanVideos(n));
  });
  obs.observe(document.body, { childList: true, subtree: true });

})();