您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Detect multiple video URLs, including M3U8 resolutions, and copy to clipboard
// ==UserScript== // @name ClipboardUrler Pro (with M3U8 Quality Parsing) // @namespace http://your.namespace // @version 3.5 // @description Detect multiple video URLs, including M3U8 resolutions, and copy to clipboard // @license MIT // @match *://*/* // @grant none // @run-at document-idle // ==/UserScript== (function () { 'use strict'; const videoExtensions = /\.(mp4|webm|mkv|avi|mov|m3u8|ts|flv|f4v|3gp|3g2|ogv|rm|rmvb|asf|wmv|m4v|vob|mpeg|mpg|m2ts|mts)(\?|#|$)/i; const fetchedManifests = new Set(); const foundURLs = new Set(); function isValidVideoURL(url) { return url && !url.startsWith('blob:') && /^https?:\/\//.test(url) && videoExtensions.test(url); } const wrapper = document.createElement('div'); Object.assign(wrapper.style, { position: 'fixed', bottom: '10px', left: '10px', zIndex: '99999', display: 'flex', gap: '6px', alignItems: 'center', background: 'rgba(255,255,255,0.95)', padding: '6px', borderRadius: '8px', boxShadow: '0 0 6px rgba(0,0,0,0.4)', fontSize: '12px', fontFamily: 'sans-serif' }); const select = document.createElement('select'); Object.assign(select.style, { fontSize: '12px', padding: '4px', maxWidth: '280px' }); const btn = document.createElement('button'); btn.textContent = '📋 Copy'; Object.assign(btn.style, { fontSize: '13px', padding: '4px 10px', cursor: 'pointer', borderRadius: '4px', border: 'none', background: '#333', color: '#fff' }); const tooltip = document.createElement('div'); tooltip.textContent = 'Copied!'; Object.assign(tooltip.style, { position: 'fixed', bottom: '40px', left: '10px', padding: '4px 8px', background: '#333', color: '#fff', borderRadius: '4px', fontSize: '12px', opacity: '0', transition: 'opacity 0.3s', zIndex: '99999', pointerEvents: 'none' }); wrapper.appendChild(select); wrapper.appendChild(btn); document.body.appendChild(wrapper); document.body.appendChild(tooltip); wrapper.style.display = 'none'; btn.onclick = () => { const url = select.value; if (url) { navigator.clipboard.writeText(url).then(() => { tooltip.style.opacity = '1'; setTimeout(() => (tooltip.style.opacity = '0'), 1000); }); } }; function addURL(url, label = '') { if (foundURLs.has(url)) return; foundURLs.add(url); const option = document.createElement('option'); option.value = url; option.textContent = label || (url.length > 60 ? url.slice(0, 60) + '...' : url); select.appendChild(option); } async function parseM3U8(masterUrl) { if (fetchedManifests.has(masterUrl)) return; fetchedManifests.add(masterUrl); try { const res = await fetch(masterUrl); const text = await res.text(); const baseUrl = masterUrl.substring(0, masterUrl.lastIndexOf('/') + 1); const lines = text.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (line.startsWith('#EXT-X-STREAM-INF')) { const nextLine = lines[i + 1]; const matchRes = line.match(/RESOLUTION=(\d+x\d+)/); const matchBw = line.match(/BANDWIDTH=(\d+)/); let label = 'M3U8 Stream'; if (matchRes) label = matchRes[1]; else if (matchBw) label = `${(parseInt(matchBw[1]) / 1000).toFixed(0)}kbps`; const finalURL = nextLine.startsWith('http') ? nextLine : baseUrl + nextLine; addURL(finalURL, label); } } } catch (e) { console.warn('Failed to parse M3U8:', e); } } function detectAllVideoURLs() { const candidates = new Set(); document.querySelectorAll('video, a[href], iframe').forEach(el => { let src = el.currentSrc || el.src || el.href || ''; if (!src || typeof src !== 'string') return; if (el.tagName === 'IFRAME') { if (src.includes('youtube.com/embed/')) { const id = src.split('/').pop().split('?')[0]; candidates.add(`https://www.youtube.com/watch?v=${id}`); } else if (src.includes('player.vimeo.com/video/')) { const id = src.split('/').pop().split('?')[0]; candidates.add(`https://vimeo.com/${id}`); } else if (isValidVideoURL(src)) { candidates.add(src); } } else if (isValidVideoURL(src)) { candidates.add(src); } }); if (candidates.size > 0) { wrapper.style.display = 'flex'; candidates.forEach(url => { addURL(url); if (url.endsWith('.m3u8')) { parseM3U8(url); } }); } else { wrapper.style.display = 'none'; } } setInterval(detectAllVideoURLs, 4000); // check every few seconds })();