搜索结果

×

搜索结果将在这里显示。

高级 M3U8 提取器(Network 级)

// ==UserScript==
// @name         高级 M3U8 提取器(Network 级)
// @namespace    https://xjrx.net/
// @version      2.1.0
// @description  劫持 fetch 和 XHR,提取页面中所有 .m3u8 请求(包括开发者工具里才能看到的)
// @author       You
// @match        *://*/*
// @grant        GM_setClipboard
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';

    const m3u8Set = new Set();

    function copyText(text) {
        GM_setClipboard(text, 'text');
        showToast('已复制: ' + text);
    }

    function showToast(msg) {
        const toast = document.createElement('div');
        Object.assign(toast.style, {
            position: 'fixed',
            left: '50%', top: '20%',
            transform: 'translateX(-50%)',
            background: 'rgba(0,0,0,0.75)',
            color: '#fff',
            padding: '8px 14px',
            borderRadius: '4px',
            fontSize: '14px',
            zIndex: 9999999,
            transition: 'opacity .3s'
        });
        toast.textContent = msg;
        document.body.appendChild(toast);
        setTimeout(() => (toast.style.opacity = '0'), 1500);
        setTimeout(() => toast.remove(), 1800);
    }

    function checkUrl(url) {
        if (url && /\.m3u8(?:\?.*)?$/i.test(url)) {
            m3u8Set.add(url);
            renderFloatBox();
        }
    }

    // 劫持 fetch
    const originalFetch = window.fetch;
    window.fetch = function (...args) {
        const url = args[0];
        checkUrl(url);
        return originalFetch.apply(this, args);
    };

    // 劫持 XMLHttpRequest
    const originalOpen = XMLHttpRequest.prototype.open;
    XMLHttpRequest.prototype.open = function (method, url) {
        checkUrl(url);
        return originalOpen.apply(this, arguments);
    };

    // 渲染悬浮窗
    let box = null;
    function renderFloatBox() {
        if (box) box.remove();
        box = document.createElement('div');
        box.id = 'm3u8-float-box';
        Object.assign(box.style, {
            position: 'fixed',
            right: '16px', bottom: '16px',
            width: '320px', maxHeight: '400px',
            background: '#1a1a1a',
            boxShadow: '0 4px 15px rgba(0,0,0,.5)',
            borderRadius: '8px',
            fontSize: '14px',
            zIndex: 999999,
            display: 'flex', flexDirection: 'column',
            fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif'
        });

        // 头部区域
        const head = document.createElement('div');
        Object.assign(head.style, {
            padding: '12px 14px', 
            borderBottom: '1px solid #333',
            background: '#252525',
            borderRadius: '8px 8px 0 0',
            display: 'flex', 
            justifyContent: 'space-between', 
            alignItems: 'center'
        });

        // 左侧:M3U8数量 + 网页标题
        const leftInfo = document.createElement('div');
        leftInfo.style.display = 'flex';
        leftInfo.style.flexDirection = 'column';
        leftInfo.style.gap = '4px';

        // M3U8数量标签
        const countLabel = document.createElement('div');
        countLabel.innerHTML = `<span style="color:#888;">M3U8</span> <strong style="color:#4CAF50;">${m3u8Set.size}</strong> 条`;
        countLabel.style.fontSize = '13px';

        // 网页标题(可点击复制)
        const pageTitle = document.createElement('div');
        const titleText = document.title || '无标题';
        pageTitle.textContent = `📄 ${titleText.length > 25 ? titleText.substring(0, 25) + '...' : titleText}`;
        pageTitle.title = titleText; // 悬停显示完整标题
        Object.assign(pageTitle.style, {
            fontSize: '12px',
            color: '#aaa',
            cursor: 'pointer',
            maxWidth: '200px',
            whiteSpace: 'nowrap',
            overflow: 'hidden',
            textOverflow: 'ellipsis',
            transition: 'color 0.2s'
        });
        pageTitle.onmouseenter = () => { pageTitle.style.color = '#4CAF50'; };
        pageTitle.onmouseleave = () => { pageTitle.style.color = '#aaa'; };
        pageTitle.onclick = () => {
            if (titleText) {
                copyText(titleText);
            } else {
                showToast('当前页面无标题');
            }
        };

        leftInfo.appendChild(countLabel);
        leftInfo.appendChild(pageTitle);

        // 右侧:关闭按钮
        const close = document.createElement('span');
        close.textContent = '×';
        Object.assign(close.style, {
            color: '#888',
            cursor: 'pointer', 
            fontSize: '22px',
            fontWeight: 'bold',
            lineHeight: '1',
            padding: '0 4px',
            transition: 'color 0.2s'
        });
        close.onmouseenter = () => { close.style.color = '#ff5555'; };
        close.onmouseleave = () => { close.style.color = '#888'; };
        close.onclick = () => box.remove();

        head.appendChild(leftInfo);
        head.appendChild(close);

        // 内容区域
        const body = document.createElement('div');
        Object.assign(body.style, {
            flex: '1', 
            overflowY: 'auto', 
            padding: '10px',
            background: '#1a1a1a'
        });

        // 自定义滚动条样式
        body.style.cssText += `
            scrollbar-width: thin;
            scrollbar-color: #444 #1a1a1a;
        `;
        body.onmouseenter = function() {
            this.style.cssText += `
                scrollbar-width: thin;
                scrollbar-color: #666 #222;
            `;
        };

        if (m3u8Set.size === 0) {
            const emptyTip = document.createElement('div');
            emptyTip.textContent = '暂无 M3U8 链接';
            Object.assign(emptyTip.style, {
                color: '#666',
                textAlign: 'center',
                padding: '30px 0',
                fontSize: '13px'
            });
            body.appendChild(emptyTip);
        } else {
            m3u8Set.forEach(url => {
                const line = document.createElement('div');
                line.style.marginBottom = '8px';
                line.style.display = 'flex';
                line.style.alignItems = 'center';

                const input = document.createElement('input');
                input.type = 'text';
                input.value = url;
                input.readOnly = true;
                Object.assign(input.style, {
                    flex: '1',
                    padding: '6px 8px',
                    border: '1px solid #333',
                    borderRadius: '4px',
                    fontSize: '11px',
                    background: '#252525',
                    color: '#e0e0e0',
                    outline: 'none'
                });

                const btn = document.createElement('button');
                btn.textContent = '复制';
                Object.assign(btn.style, {
                    width: '52px', 
                    padding: '6px 0',
                    marginLeft: '6px',
                    fontSize: '12px', 
                    cursor: 'pointer',
                    background: 'linear-gradient(135deg, #4CAF50, #45a049)',
                    color: '#fff',
                    border: 'none',
                    borderRadius: '4px',
                    fontWeight: '500',
                    transition: 'all 0.2s'
                });
                btn.onmouseenter = () => {
                    btn.style.background = 'linear-gradient(135deg, #5CBF60, #4CAF50)';
                    btn.style.transform = 'scale(1.02)';
                };
                btn.onmouseleave = () => {
                    btn.style.background = 'linear-gradient(135deg, #4CAF50, #45a049)';
                    btn.style.transform = 'scale(1)';
                };
                btn.onclick = () => copyText(url);

                line.appendChild(input);
                line.appendChild(btn);
                body.appendChild(line);
            });
        }

        box.appendChild(head);
        box.appendChild(body);
        document.body.appendChild(box);
    }

    // 页面加载后延迟渲染
    if (document.body) {
        renderFloatBox();
    } else {
        document.addEventListener('DOMContentLoaded', renderFloatBox);
    }
})();
发布时间: