浏览器插件-视频30倍速播放

background.js

​

// 监听标签页更新
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
  if (changeInfo.status === 'complete' && tab.url) {
    // 检查是否启用了嗅探
    chrome.storage.local.get(['snifferEnabled'], function(result) {
      if (result.snifferEnabled) {
        // 通知内容脚本开始嗅探
        chrome.tabs.sendMessage(tabId, {
          action: 'updateSniffer',
          enabled: true
        });
      }
    });
  }
});

// 安装或更新插件时初始化设置
chrome.runtime.onInstalled.addListener(() => {
  chrome.storage.local.set({
    snifferEnabled: false,
    playbackSpeed: 1.0,
    detectedVideos: []
  });
});

​

content.js

// 全局变量
let snifferEnabled = false;
let playbackSpeed = 1.0;
let detectedVideos = [];
let selectedVideoIndex = -1;
let observer = null;

// 初始化
function initialize() {
  // 从存储中获取设置
  chrome.storage.local.get(['snifferEnabled', 'playbackSpeed'], function(result) {
    snifferEnabled = result.snifferEnabled || false;
    playbackSpeed = result.playbackSpeed || 1.0;
    
    if (snifferEnabled) {
      startVideoSniffer();
    }
  });
  
  // 监听来自popup的消息
  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.action === 'updateSniffer') {
      snifferEnabled = request.enabled;
      if (snifferEnabled) {
        startVideoSniffer();
      } else {
        stopVideoSniffer();
      }
    } else if (request.action === 'updateSpeed') {
      playbackSpeed = request.speed;
      applyPlaybackSpeed();
    } else if (request.action === 'selectVideo') {
      selectedVideoIndex = request.index;
      applyPlaybackSpeed();
    }
  });
}

// 开始视频嗅探
function startVideoSniffer() {
  // 立即扫描当前页面上的视频
  scanForVideos();
  
  // 设置MutationObserver监听DOM变化
  if (!observer) {
    observer = new MutationObserver(function(mutations) {
      scanForVideos();
    });
    
    observer.observe(document.body, {
      childList: true,
      subtree: true
    });
  }
}

// 停止视频嗅探
function stopVideoSniffer() {
  if (observer) {
    observer.disconnect();
    observer = null;
  }
}

// 扫描页面上的视频
function scanForVideos() {
  if (!snifferEnabled) return;
  
  const videos = Array.from(document.querySelectorAll('video'));
  const iframes = Array.from(document.querySelectorAll('iframe'));
  
  // 处理直接的video标签
  detectedVideos = videos.map(video => ({
    element: video,
    src: video.src || (video.querySelector('source') ? video.querySelector('source').src : '未知源')
  }));
  
  // 尝试处理iframe中的视频(受同源策略限制,可能无法访问)
  iframes.forEach(iframe => {
    try {
      const iframeVideos = Array.from(iframe.contentDocument.querySelectorAll('video'));
      detectedVideos = detectedVideos.concat(iframeVideos.map(video => ({
        element: video,
        src: video.src || (video.querySelector('source') ? video.querySelector('source').src : '未知源')
      })));
    } catch (e) {
      // 跨域iframe无法访问,忽略错误
    }
  });
  
  // 通知popup更新视频列表
  chrome.runtime.sendMessage({
    action: 'updateVideoList',
    videos: detectedVideos.map(v => ({src: v.src}))
  });
  
  // 应用播放速度
  applyPlaybackSpeed();
}

// 应用播放速度
function applyPlaybackSpeed() {
  if (detectedVideos.length === 0) return;
  
  if (selectedVideoIndex >= 0 && selectedVideoIndex < detectedVideos.length) {
    // 只调整选中的视频
    const video = detectedVideos[selectedVideoIndex].element;
    video.playbackRate = playbackSpeed;
  } else {
    // 调整所有检测到的视频
    detectedVideos.forEach(video => {
      video.element.playbackRate = playbackSpeed;
    });
  }
}

// 初始化插件
initialize();

manifest.json

{
  "manifest_version": 3,
  "name": "视频嗅探与倍速控制",
  "version": "1.0",
  "description": "自动嗅探网站视频并控制播放速度(0.033x-30x)",
  "permissions": ["activeTab", "storage", "scripting"],
  "action": {
    "default_popup": "popup.html",
    "default_icon": {
      "16": "images/icon16.png",
      "48": "images/icon48.png",
      "128": "images/icon128.png"
    }
  },
  "icons": {
    "16": "images/icon16.png",
    "48": "images/icon48.png",
    "128": "images/icon128.png"
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"],
      "run_at": "document_idle"
    }
  ],
  "background": {
    "service_worker": "background.js"
  }
}

popup.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>视频嗅探与倍速控制</title>
  <style>
    body {
      width: 300px;
      padding: 10px;
      font-family: Arial, sans-serif;
    }
    .container {
      display: flex;
      flex-direction: column;
      gap: 15px;
    }
    .switch {
      position: relative;
      display: inline-block;
      width: 60px;
      height: 34px;
    }
    .switch input {
      opacity: 0;
      width: 0;
      height: 0;
    }
    .slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      transition: .4s;
      border-radius: 34px;
    }
    .slider:before {
      position: absolute;
      content: "";
      height: 26px;
      width: 26px;
      left: 4px;
      bottom: 4px;
      background-color: white;
      transition: .4s;
      border-radius: 50%;
    }
    input:checked + .slider {
      background-color: #2196F3;
    }
    input:checked + .slider:before {
      transform: translateX(26px);
    }
    .control-group {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    .video-list {
      max-height: 200px;
      overflow-y: auto;
      border: 1px solid #ddd;
      border-radius: 4px;
      padding: 5px;
    }
    .video-item {
      padding: 5px;
      border-bottom: 1px solid #eee;
      cursor: pointer;
    }
    .video-item:hover {
      background-color: #f5f5f5;
    }
    .speed-control {
      display: flex;
      align-items: center;
      gap: 10px;
    }
    .speed-value {
      width: 50px;
      text-align: center;
    }
  </style>
</head>
<body>
  <div class="container">
    <h2>视频嗅探与倍速控制</h2>
    
    <div class="control-group">
      <span>视频嗅探</span>
      <label class="switch">
        <input type="checkbox" id="sniffer-toggle">
        <span class="slider"></span>
      </label>
    </div>
    
    <div class="speed-control">
      <span>播放速度:</span>
      <input type="range" id="speed-slider" min="0.033" max="30" step="0.1" value="1">
      <span id="speed-value" class="speed-value">1.0x</span>
    </div>
    
    <div>
      <h3>检测到的视频:</h3>
      <div id="video-list" class="video-list">
        <div class="video-item">暂无检测到视频</div>
      </div>
    </div>
  </div>
  <script src="popup.js"></script>
</body>
</html>

popup.js

document.addEventListener('DOMContentLoaded', function() {
  const snifferToggle = document.getElementById('sniffer-toggle');
  const speedSlider = document.getElementById('speed-slider');
  const speedValue = document.getElementById('speed-value');
  const videoList = document.getElementById('video-list');
  
  // 加载保存的设置
  chrome.storage.local.get(['snifferEnabled', 'playbackSpeed', 'detectedVideos'], function(result) {
    snifferToggle.checked = result.snifferEnabled || false;
    
    const speed = result.playbackSpeed || 1;
    speedSlider.value = speed;
    speedValue.textContent = speed.toFixed(1) + 'x';
    
    updateVideoList(result.detectedVideos || []);
  });
  
  // 监听嗅探开关变化
  snifferToggle.addEventListener('change', function() {
    const enabled = snifferToggle.checked;
    chrome.storage.local.set({snifferEnabled: enabled});
    
    // 通知内容脚本更新嗅探状态
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
      chrome.tabs.sendMessage(tabs[0].id, {
        action: 'updateSniffer',
        enabled: enabled
      });
    });
  });
  
  // 监听速度滑块变化
  speedSlider.addEventListener('input', function() {
    const speed = parseFloat(speedSlider.value);
    speedValue.textContent = speed.toFixed(1) + 'x';
    chrome.storage.local.set({playbackSpeed: speed});
    
    // 通知内容脚本更新播放速度
    chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
      chrome.tabs.sendMessage(tabs[0].id, {
        action: 'updateSpeed',
        speed: speed
      });
    });
  });
  
  // 更新视频列表
  function updateVideoList(videos) {
    if (!videos || videos.length === 0) {
      videoList.innerHTML = '<div class="video-item">暂无检测到视频</div>';
      return;
    }
    
    videoList.innerHTML = '';
    videos.forEach((video, index) => {
      const videoItem = document.createElement('div');
      videoItem.className = 'video-item';
      videoItem.textContent = `视频 ${index + 1}: ${video.src.substring(0, 50)}...`;
      videoItem.addEventListener('click', function() {
        chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
          chrome.tabs.sendMessage(tabs[0].id, {
            action: 'selectVideo',
            index: index
          });
        });
      });
      videoList.appendChild(videoItem);
    });
  }
  
  // 监听来自内容脚本的消息
  chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
    if (request.action === 'updateVideoList') {
      chrome.storage.local.set({detectedVideos: request.videos});
      updateVideoList(request.videos);
    }
  });
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值