// ==UserScript== // @name HLS Playback Optimizer with P2P(视频协议优化HLS篇) // @namespace http://tampermonkey.net/ // @version 1.3 // @description Optimize HLS playback on web pages with P2P assistance and add online/offline detection. // @author KiwiFruit // @match *://*/* // @grant none // @license MIT // @downloadURL https://update.greasyfork.icu/scripts/529606/HLS%20Playback%20Optimizer%20with%20P2P%EF%BC%88%E8%A7%86%E9%A2%91%E5%8D%8F%E8%AE%AE%E4%BC%98%E5%8C%96HLS%E7%AF%87%EF%BC%89.user.js // @updateURL https://update.greasyfork.icu/scripts/529606/HLS%20Playback%20Optimizer%20with%20P2P%EF%BC%88%E8%A7%86%E9%A2%91%E5%8D%8F%E8%AE%AE%E4%BC%98%E5%8C%96HLS%E7%AF%87%EF%BC%89.meta.js // ==/UserScript== /* global Hls, SimplePeer */ (function() { 'use strict'; function loadExternalScript(url, callback) { var script = document.createElement('script'); script.src = url; script.onload = callback; document.head.appendChild(script); } // Load external scripts for hls.js and simple-peer loadExternalScript('https://unpkg.com/hls.js@latest/dist/hls.min.js', function() { console.log('hls.js loaded successfully.'); // 在这里可以使用 hls 对象 }); loadExternalScript('https://unpkg.com/simple-peer@latest/dist/simple-peer.min.js', function() { console.log('simple-peer loaded successfully.'); // 在这里可以使用 simplePeer 对象 }); function getSegmentData(frag) { const segmentUrl = frag.url; return fetch(segmentUrl) .then(response => response.arrayBuffer()) .then(data => { // Convert ArrayBuffer to Blob or other data format as needed const blob = new Blob([data]); return blob; }) .catch(error => { console.error('Failed to get segment data:', error); // Handle error, e.g., by skipping the segment }); } function optimizeHLSPlaybackWithP2P(videoElement) { const hls = new Hls(); hls.attachMedia(videoElement); hls.loadSource('http://example.com/path/to/your/playlist.m3u8'); const peer = new SimplePeer({ initiator: location.hash === '#init', trickle: false, wrtc: {} }); let peers = []; peer.on('signal', (data) => { console.log('Signal:', data); if (location.hash !== '#init') { location.hash = JSON.stringify(data); } else { document.getElementById('join').onclick = () => { peer.signal(JSON.parse(location.hash.substr(1))); }; } }); peer.on('connect', () => { console.log('Connected to peer'); peers.push(peer); }); peer.on('data', (data) => { console.log('Received data from peer:', data); // Handle the received segment and append it to the buffer }); let bufferedSegments = []; let currentBufferLength = 0; hls.on(Hls.Events.BUFFER_APPENDING, (event, data) => { if (!bufferedSegments.includes(data.frag.sn)) { bufferedSegments.push(data.frag.sn); currentBufferLength += data.frag.duration; } }); hls.on(Hls.Events.FRAG_BUFFERED, (event, data) => { while (currentBufferLength > 90) { // Adjust this threshold as needed const oldestSegment = bufferedSegments.shift(); hls.remove(oldestSegment); currentBufferLength -= oldestSegment.duration; } const segmentData = getSegmentData(data.frag); peers.forEach(p => p.send(segmentData)); }); function preloadNextSegments() { const networkSpeed = hls.network.speed; // Hypothetical property, adjust according to your implementation const segmentsToPreload = Math.max(2, Math.floor(networkSpeed / 5)); for (let i = 0; i < segmentsToPreload; i++) { hls.nextLoadLevel = i; hls.startLoad(); } } setInterval(preloadNextSegments, 5000); } // Online/Offline detection window.addEventListener('online', function() { console.log('You are online'); // Optionally reload the video source or retry failed requests const videoElements = document.querySelectorAll('video'); videoElements.forEach(videoElement => { if (videoElement.src.endsWith('.m3u8')) { optimizeHLSPlaybackWithP2P(videoElement); } else if (videoElement.querySelector('source[src$=".m3u8"]')) { optimizeHLSPlaybackWithP2P(videoElement.querySelector('source[src$=".m3u8"]').parentNode); } }); }); window.addEventListener('offline', function() { console.log('You are offline'); // Optionally pause the video or show a message to the user const videoElements = document.querySelectorAll('video'); videoElements.forEach(videoElement => { videoElement.pause(); }); }); // Run the optimization function when the script loads const videoElements = document.querySelectorAll('video'); videoElements.forEach(videoElement => { if (videoElement.src.endsWith('.m3u8')) { optimizeHLSPlaybackWithP2P(videoElement); } else if (videoElement.querySelector('source[src$=".m3u8"]')) { optimizeHLSPlaybackWithP2P(videoElement.querySelector('source[src$=".m3u8"]').parentNode); } }); })();