Greasy Fork

来自缓存

Greasy Fork is available in English.

VideoControlReset

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

// ==UserScript==
// @name         VideoControlReset
// @namespace    http://greasyfork.icu/
// @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 });

})();