资源嗅探

资源嗅探工具,支持 M3U8 解析下载,优化 UI,增强交互体验

目前为 2025-01-31 提交的版本。查看 最新版本

// ==UserScript==
// @name         资源嗅探
// @namespace    https://staybrowser.com/
// @version      1.1.1
// @description  资源嗅探工具,支持 M3U8 解析下载,优化 UI,增强交互体验
// @author       GaryIndex
// @license      telegram @GaryIndex
// @match        *://*/*
// @grant        none
// @icon         https://raw.githubusercontent.com/GaryIndex/GaryIndex/refs/heads/main/GaryIndex.JPG
// ==/UserScript==

(() => {
    'use strict';

    const snifferBtn = document.createElement('button');
    snifferBtn.innerText = '资源嗅探';
    Object.assign(snifferBtn.style, {
        position: 'fixed', bottom: '20px', right: '20px', zIndex: '10000',
        padding: '12px 20px', fontSize: '16px', border: 'none',
        borderRadius: '25px', backgroundColor: '#007aff', color: '#fff',
        cursor: 'pointer', boxShadow: '0 4px 8px rgba(0,0,0,0.2)'
    });

    const modal = document.createElement('div');
    Object.assign(modal.style, {
        position: 'fixed', width: '90%', height: '50vh', left: '5%', top: '25vh',
        backgroundColor: '#fff', borderRadius: '12px', boxShadow: '0 4px 10px rgba(0,0,0,0.3)',
        display: 'none', flexDirection: 'column', zIndex: '10001', overflow: 'hidden'
    });

    const modalHeader = document.createElement('div');
    Object.assign(modalHeader.style, {
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        padding: '12px 16px', fontSize: '16px', fontWeight: 'bold'
    });
    modalHeader.innerHTML = `<span>选择要下载的资源</span><span style="cursor:pointer;font-weight:bold;color:#007aff;">关闭</span>`;
    modalHeader.lastElementChild.onclick = () => { modal.style.display = 'none'; snifferBtn.style.display = 'block'; };

    const categories = ['全部', '视频', '音频', '图片', '文档', '文件扩展', '其他'];
    let activeCategoryIndex = 0; 
    const categoryBar = document.createElement('div');
    Object.assign(categoryBar.style, {
        display: 'flex', overflowX: 'auto', padding: '10px', gap: '10px'
    });

    categories.forEach((cat, index) => {
        const btn = document.createElement('button');
        btn.innerText = cat;
        btn.dataset.index = index;
        Object.assign(btn.style, {
            padding: '8px 12px', border: 'none', borderRadius: '20px',
            backgroundColor: index === 0 ? '#007aff' : '#f0f0f0', 
            color: index === 0 ? '#fff' : '#000',
            cursor: 'pointer', whiteSpace: 'nowrap'
        });
        btn.onclick = (e) => {
            activeCategoryIndex = parseInt(e.target.dataset.index);
            filterResources(cat);
        };
        categoryBar.appendChild(btn);
    });

    const resourceList = document.createElement('div');
    Object.assign(resourceList.style, { flex: 1, overflowY: 'auto', padding: '10px' });

    modal.appendChild(modalHeader);
    modal.appendChild(categoryBar);
    modal.appendChild(resourceList);
    document.body.appendChild(snifferBtn);
    document.body.appendChild(modal);

    snifferBtn.onclick = () => {
        updateResourceList();
        modal.style.display = 'flex';
        snifferBtn.style.display = 'none';
    };

    let allResources = [];
    function updateResourceList() {
        allResources = [];
        document.querySelectorAll('a, source, link, video, audio').forEach(el => {
            const url = el.href || el.src;
            if (url) {
                let type = '其他';
                if (url.match(/\.(mp4|avi|mov|mkv|webm|flv|wmv|mpeg|mpg|3gp|ts|rm|rmvb|ogv|asf|vob|f4v|m4v|qt|h264|hevc|mpeg-2|mpeg-4|divx|xvid|mov|mts|tp|dat|yuv|m3u8|mkv|webm|hls|mpd|dash|ts|f4v)$/)) type = '视频';
                else if (url.match(/\.(mp3|wav|aac|flac|ogg|wma|m4a|opus|alac|ape|dsd|pcm|aiff|au|mid|midi|amr|caf|voc|ra|rm|mpc|tta)$/)) type = '音频';
                else if (url.match(/\.(jpeg|jpg|png|gif|bmp|tiff|tif|webp|heif|heic|jpeg 2000|jp2|j2k|apng|tga|tpic|dds|exr|hdr|raw|svg|ai|eps|pdf|cdr|fig|skp|psd|xcf|ico|icns|stl|obj|ply|dicom|shp|pcx|pict|pct|iff|jbig|jbg|sgi)$/)) type = '图片';
                else if (url.match(/\.(.py|.js|.java|.c|.cpp|.cc|.cxx|.cs|.go|.rb|.php|.swift|.kt|.kts|.ts|.html|.htm|.css|.json|.xml|.sql|.r|.m|.sh|.bash|.ps1|.rs|.lua|.hs|.scala|.tex|.md|.vhd|.vhdl|.asm|.pl|.dart|.el|.erl|.ex|.exs|.jl|.lisp|.ml|.nim|.pas|.pde|.psm1|.rpy|.sml|.tcl|.v|.vbs|.wsf|.yml|.yaml|.coffee|.graphql|.sh|.sql|.scss|.rmd|.styl|.pug|.vue|.handlebars|.twig|.hbs|.asp|.aspx|.cgi|.pl|.psd|.ai|.indd|.abap|.actionscript|.ada|.awk|.batch|.bc|.bh|.bzl|.capnp|.clj|.cljc|.cobol|.coffee|.cql|.cshtml|.cu|.d|.dats|.db|.dcm|.dif|.dtd|.dylib|.f|.f90|.fd|.fxml|.glsl|.h|.hxx|.hpp|.hx|.idl|.inl|.install|.java|.jl|.l|.lisp|.liquid|.lua|.m4|.makefile|.map|.maven|.ml|.mli|.nim|.ninja|.nvm|.objc|.pl|.pm|.ps|.puml|.py|.q|.r|.rexx|.rst|.rs|.scala|.scm|.sh|.shtml|.sml|.sol|.ss|.svg|.tcl|.tex|.ts|.tsx|.v|.vhdl|.vim|.xhtml|.xml|.xsl|.yaml|.yml)$/)) type = '文件扩展';
                else if (url.match(/\.(pdf|doc|docx|ppt|pptx|xls|xlsx|txt|rtf|odt|ods|odp|epub|mobi|azw3|chm|djvu|tex|md|html|xps|pages|key|numbers|csv|tsv|epub3|fb2|azw|abw)$/)) type = '文档';

                allResources.push({ name: formatName(url), url, type });
            }
        });
        filterResources(categories[activeCategoryIndex]);
    }

    function filterResources(category) {
        resourceList.innerHTML = '';
        const filtered = category === '全部' ? allResources : allResources.filter(res => res.type === category);

        if (filtered.length === 0) {
            resourceList.innerHTML = `<p style="color:#666; text-align:center;">暂无资源可供下载</p>`;
        } else {
            filtered.forEach(res => {
                if (!res.name) return;
                const item = document.createElement('div');
                Object.assign(item.style, {
                    display: 'flex', justifyContent: 'space-between', alignItems: 'center',
                    padding: '8px'
                });

                const name = document.createElement('span');
                name.innerText = res.name;
                name.style.flex = '1';
                name.style.textAlign = 'left'; 

                const downloadBtn = document.createElement('button');
                downloadBtn.innerText = '下载';
                Object.assign(downloadBtn.style, {
                    padding: '6px 12px', border: 'none', borderRadius: '15px',
                    backgroundColor: '#007aff', color: '#fff', cursor: 'pointer'
                });
                downloadBtn.onclick = () => downloadResource(res.url, res.name);

                item.appendChild(name);
                item.appendChild(downloadBtn);
                resourceList.appendChild(item);
            });
        }

        categoryBar.querySelectorAll('button').forEach((btn, index) => {
            btn.style.backgroundColor = index === activeCategoryIndex ? '#007aff' : '#f0f0f0';
            btn.style.color = index === activeCategoryIndex ? '#fff' : '#000';
        });

        setTimeout(() => {
            categoryBar.scrollLeft = categoryBar.children[activeCategoryIndex].offsetLeft - 20;
        }, 0);
    }

    function downloadResource(url, filename) {
        if (url.endsWith('.m3u8')) {
            downloadM3U8(url, filename);
            return;
        }

        fetch(url)
            .then(response => {
                const contentType = response.headers.get('content-type') || '';
                return response.blob().then(blob => ({ blob, contentType }));
            })
            .then(({ blob, contentType }) => {
                const a = document.createElement('a');
                a.href = URL.createObjectURL(blob);

                const forcedExtensions = [
                    '.md', '.json', '.xml', '.csv', '.yaml', '.yml', '.sql', 
                    '.sh', '.py', '.js', '.ts', '.html', '.css', '.jsx', '.tsx',
                    '.c', '.cpp', '.java', '.go', '.rb', '.php', '.swift', '.kt', 
                    '.r', '.lua', '.pl', '.dart', '.scala', '.vhd', '.asm', '.ps1'
                ];

                const extIndex = filename.lastIndexOf('.');
                let ext = extIndex !== -1 ? filename.slice(extIndex).toLowerCase() : '';

                const randomNum = Math.floor(100 + Math.random() * 900); 

                if (!ext) {
                    ext = '.txt';
                    filename += ext;
                }

                if (!filename.trim()) {
                    filename = `downloaded_file${randomNum}.txt`;
                }

                if (forcedExtensions.includes(ext) || contentType.startsWith('text/')) {
                    a.download = filename;
                } else {
                    a.download = filename;
                }

                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            })
            .catch(() => alert('资源下载失败'));
    }

    function downloadM3U8(url, filename) {
        fetch(url)
            .then(response => response.text())
            .then(data => {
                const m3u8Blob = new Blob([data], { type: 'application/x-mpegURL' });
                const m3u8Url = URL.createObjectURL(m3u8Blob);
                const a = document.createElement('a');
                a.href = m3u8Url;
                a.download = filename.endsWith('.m3u8') ? filename : filename + '.m3u8';
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
            })
            .catch(() => alert('M3U8 下载失败'));
    }

    function formatName(url) {
        const name = url.split('/').pop().split('?')[0];
        if (name.length <= 15) return name;
        
        const extIndex = name.lastIndexOf('.');
        if (extIndex !== -1) {
            const ext = name.slice(extIndex); 
            const base = name.slice(0, 15); 
            return base + ext;
        }
        return name.slice(0, 15); 
    }

})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址