// ==UserScript== // @name 资源嗅探器 Pro v4 // @namespace http://tampermonkey.net/ // @version 4.0 // @description 强大的网页资源嗅探工具,支持自动检测、分类展示、预览和下载各类网页资源 // @author CodeBuddy // @match *://*/* // @grant GM_download // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @run-at document-start // @downloadURL none // ==/UserScript== class ResourceSniffer { constructor() { // 配置选项 this.config = { // 支持的资源类型 resourceTypes: { image: { enabled: true, extensions: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg', 'tiff'], icon: '📷' }, video: { enabled: true, extensions: ['mp4', 'webm', 'avi', 'mov', 'flv', 'wmv', 'mkv'], icon: '🎬' }, audio: { enabled: true, extensions: ['mp3', 'wav', 'flac', 'aac', 'ogg', 'wma'], icon: '🎵' }, document: { enabled: true, extensions: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'csv'], icon: '📄' }, other: { enabled: true, icon: '📦' } }, // 面板位置和大小 panel: { width: '350px', height: '600px', left: '20px', top: '100px', opacity: 0.95 }, // 其他配置 maxResources: 500, ignoreSmallResources: true, minResourceSize: 1024, // 1KB updateInterval: 5000 // 5秒更新一次UI }; // 全局变量 this.resources = new Map(); // 存储嗅探到的资源 this.panelVisible = false; // 面板可见性 this.activeTab = 'all'; // 当前激活的标签 this.panelElement = null; // 面板元素 this.toggleButton = null; // 切换按钮 this.resourceCount = 0; // 资源计数 this.isDragging = false; // 是否正在拖拽 this.dragOffset = { x: 0, y: 0 }; // 拖拽偏移量 this.previewModal = null; // 预览模态框 this.lastUpdateTime = 0; // 上次更新时间 // 初始化 this.init(); } // 初始化函数 init() { // 确保文档就绪后初始化 this.checkDocumentReady(); // 拦截请求以嗅探资源 this.interceptRequests(); // 监听页面上的媒体元素 this.monitorMediaElements(); } // 检查文档是否就绪 checkDocumentReady() { if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => this.onDocumentReady()); } else { // 延迟一点执行,确保body已完全加载 setTimeout(() => this.onDocumentReady(), 300); } } // 文档就绪后执行 onDocumentReady() { console.log('资源嗅探器 Pro v4 已加载'); // 创建悬浮按钮 this.createToggleButton(); // 创建样式 this.injectStyles(); // 定期更新UI setInterval(() => this.updateUI(), this.config.updateInterval); } // 注入样式 injectStyles() { GM_addStyle(` /* 面板样式 */ #resource-sniffer-panel { position: fixed; width: ${this.config.panel.width}; height: ${this.config.panel.height}; left: ${this.config.panel.left}; top: ${this.config.panel.top}; background: #1e1e1e; border-radius: 8px; box-shadow: 0 0 15px rgba(0, 0, 0, 0.5); z-index: 30000; display: flex; flex-direction: column; opacity: ${this.config.panel.opacity}; transition: opacity 0.3s; font-family: 'Microsoft YaHei', Arial, sans-serif; } /* 面板头部 */ #sniffer-panel-header { padding: 10px 15px; background: #2d2d2d; border-top-left-radius: 8px; border-top-right-radius: 8px; display: flex; justify-content: space-between; align-items: center; cursor: move; } #panel-title { color: white; font-size: 14px; font-weight: bold; } #panel-controls { display: flex; gap: 8px; } .panel-btn { background: none; border: none; color: white; cursor: pointer; width: 24px; height: 24px; display: flex; justify-content: center; align-items: center; border-radius: 4px; transition: background 0.2s; } .panel-btn:hover { background: rgba(255, 255, 255, 0.1); } /* 标签栏 */ #sniffer-tabs { display: flex; background: #252526; overflow-x: auto; white-space: nowrap; border-bottom: 1px solid #373737; } .tab-btn { padding: 8px 15px; color: #d4d4d4; background: none; border: none; cursor: pointer; font-size: 12px; transition: all 0.2s; display: flex; align-items: center; gap: 5px; } .tab-btn.active { color: white; background: #1e1e1e; border-bottom: 2px solid #0078d7; } .tab-btn:hover:not(.active) { background: rgba(255, 255, 255, 0.05); } /* 资源列表 */ #resources-container { flex: 1; overflow-y: auto; padding: 10px; } #resources-list { list-style: none; padding: 0; margin: 0; } .resource-item { background: #2d2d2d; border-radius: 6px; margin-bottom: 10px; overflow: hidden; transition: transform 0.2s, box-shadow 0.2s; } .resource-item:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3); } .resource-header { padding: 8px 12px; display: flex; justify-content: space-between; align-items: center; background: #252526; cursor: pointer; } .resource-title { color: white; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; margin-right: 10px; } .resource-size { color: #999; font-size: 12px; margin-right: 10px; } .resource-type-badge { padding: 2px 6px; border-radius: 4px; font-size: 11px; color: white; display: flex; align-items: center; gap: 3px; } .type-image { background: #0078d7; } .type-video { background: #00bcf2; } .type-audio { background: #7c7cd9; } .type-document { background: #d83b01; } .type-other { background: #515151; } .resource-content { padding: 10px; display: none; } .resource-preview-container { width: 100%; height: 180px; background: #1e1e1e; border-radius: 4px; margin-bottom: 10px; display: flex; justify-content: center; align-items: center; overflow: hidden; position: relative; } .resource-thumbnail { width: 100%; height: 100%; object-fit: contain; } .resource-actions { display: flex; gap: 10px; } .resource-btn { flex: 1; padding: 8px 12px; border: none; border-radius: 4px; cursor: pointer; font-size: 13px; font-weight: bold; transition: background 0.2s; } .preview { background: #0078d7; color: white; } .preview:hover { background: #005a9e; } .download { background: #00b42a; color: white; } .download:hover { background: #008c22; } .resource-url { margin-top: 10px; padding: 8px; background: #1e1e1e; border-radius: 4px; font-size: 12px; color: #999; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 空状态 */ #empty-state { display: flex; flex-direction: column; justify-content: center; align-items: center; height: 100%; color: #666; text-align: center; } #empty-state svg { width: 64px; height: 64px; margin-bottom: 15px; opacity: 0.3; } /* 预览模态框 */ #preview-modal { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.8); display: none; justify-content: center; align-items: center; z-index: 30000; flex-direction: column; } #preview-modal .modal-content { background: #1e1e1e; border-radius: 8px; max-width: 90%; max-height: 90%; overflow: hidden; position: relative; } #preview-modal .modal-header { padding: 10px 15px; background: #2d2d2d; color: white; display: flex; justify-content: space-between; align-items: center; } #preview-modal .preview-title { font-size: 14px; font-weight: bold; } #preview-modal .close-btn { background: none; border: none; color: white; font-size: 18px; cursor: pointer; } #preview-modal .preview-body { padding: 10px; max-height: 70vh; overflow: auto; display: flex; justify-content: center; align-items: center; } #preview-modal img, #preview-modal video, #preview-modal audio { max-width: 100%; max-height: 70vh; } /* 切换按钮 */ #resource-sniffer-toggle { position: fixed; width: 50px; height: 50px; border-radius: 50%; background: linear-gradient(135deg, #0078d7, #00bcf2); color: white; border: none; cursor: pointer; font-size: 20px; z-index: 29999; box-shadow: 0 5px 15px rgba(0, 120, 215, 0.3); display: flex; justify-content: center; align-items: center; right: 20px; bottom: 20px; transition: all 0.3s; } #resource-sniffer-toggle:hover { transform: scale(1.1); box-shadow: 0 8px 20px rgba(0, 120, 215, 0.4); } #resource-sniffer-toggle .resource-count { position: absolute; top: -5px; right: -5px; background: #ff3b30; color: white; border-radius: 50%; width: 20px; height: 20px; font-size: 12px; display: flex; justify-content: center; align-items: center; font-weight: bold; border: 2px solid white; } /* 滚动条样式 */ ::-webkit-scrollbar { width: 8px; height: 8px; } ::-webkit-scrollbar-track { background: #2d2d2d; } ::-webkit-scrollbar-thumb { background: #555; border-radius: 4px; } ::-webkit-scrollbar-thumb:hover { background: #777; } `); } // 创建切换按钮 createToggleButton() { if (!document.body) { console.error('document.body 不存在,无法创建切换按钮'); return; } // 避免重复创建 if (this.toggleButton) { return; } try { this.toggleButton = document.createElement('button'); this.toggleButton.id = 'resource-sniffer-toggle'; this.toggleButton.innerHTML = ` 🕵️ 0 `; this.toggleButton.title = '资源嗅探器 Pro v4'; // 添加点击事件 this.toggleButton.addEventListener('click', () => this.togglePanel()); // 添加到页面 document.body.appendChild(this.toggleButton); console.log('切换按钮已创建'); } catch (error) { console.error('创建切换按钮失败:', error); // 尝试延迟后重试 setTimeout(() => this.createToggleButton(), 500); } } // 切换面板显示/隐藏 togglePanel() { this.panelVisible = !this.panelVisible; if (this.panelVisible) { this.createPanel(); this.panelElement.style.display = 'flex'; } else { if (this.panelElement) { this.panelElement.style.display = 'none'; } } } // 创建面板 createPanel() { if (this.panelElement) { return; } // 确保document.body已加载 if (!document.body) { console.error('document.body 不存在,无法创建面板'); setTimeout(() => this.createPanel(), 500); return; } try { // 创建面板元素 this.panelElement = document.createElement('div'); this.panelElement.id = 'resource-sniffer-panel'; this.panelElement.style.display = 'none'; // 面板头部 const header = document.createElement('div'); header.id = 'sniffer-panel-header'; header.innerHTML = `
资源嗅探器 Pro v4
`; // 标签栏 const tabs = document.createElement('div'); tabs.id = 'sniffer-tabs'; // 添加所有标签 let tabsHTML = ''; for (const [type, config] of Object.entries(this.config.resourceTypes)) { if (config.enabled) { tabsHTML += ``; } } tabs.innerHTML = tabsHTML; // 资源列表容器 const resourcesContainer = document.createElement('div'); resourcesContainer.id = 'resources-container'; // 空状态 const emptyState = document.createElement('div'); emptyState.id = 'empty-state'; emptyState.innerHTML = `
暂无检测到的资源
访问网页时会自动检测资源
`; resourcesContainer.appendChild(emptyState); // 资源列表 const resourcesList = document.createElement('ul'); resourcesList.id = 'resources-list'; resourcesContainer.appendChild(resourcesList); // 组装面板 this.panelElement.appendChild(header); this.panelElement.appendChild(tabs); this.panelElement.appendChild(resourcesContainer); // 添加到页面 document.body.appendChild(this.panelElement); // 添加标签点击事件 document.querySelectorAll('.tab-btn').forEach(btn => { btn.addEventListener('click', () => { // 移除所有激活状态 document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active')); // 添加当前激活状态 btn.classList.add('active'); // 更新当前标签 this.activeTab = btn.dataset.tab; // 更新资源列表 this.updateResourceList(); }); }); // 添加面板控制事件 document.getElementById('close-btn').addEventListener('click', () => { this.panelVisible = false; this.panelElement.style.display = 'none'; }); document.getElementById('minimize-btn').addEventListener('click', () => { this.panelElement.style.height = '40px'; tabs.style.display = 'none'; resourcesContainer.style.display = 'none'; }); document.getElementById('refresh-btn').addEventListener('click', () => { this.updateResourceList(); }); // 添加拖拽功能 header.addEventListener('mousedown', (e) => { this.isDragging = true; this.dragOffset.x = e.clientX - this.panelElement.getBoundingClientRect().left; this.dragOffset.y = e.clientY - this.panelElement.getBoundingClientRect().top; }); document.addEventListener('mousemove', (e) => { if (!this.isDragging) return; e.preventDefault(); const x = e.clientX - this.dragOffset.x; const y = e.clientY - this.dragOffset.y; this.panelElement.style.left = `${x}px`; this.panelElement.style.top = `${y}px`; }); document.addEventListener('mouseup', () => { this.isDragging = false; }); console.log('面板已创建'); } catch (error) { console.error('创建面板失败:', error); // 尝试延迟后重试 setTimeout(() => this.createPanel(), 500); } } // 更新UI updateUI() { const now = Date.now(); if (now - this.lastUpdateTime < this.config.updateInterval) { return; } this.lastUpdateTime = now; // 更新资源计数 this.updateResourceCount(); // 如果面板可见,更新资源列表 if (this.panelVisible) { this.updateResourceList(); } } // 更新资源计数 updateResourceCount() { const count = this.resources.size; this.resourceCount = count; // 更新按钮上的计数 if (this.toggleButton) { const countElement = this.toggleButton.querySelector('.resource-count'); if (countElement) { countElement.textContent = count; } } } // 更新资源列表 updateResourceList() { const resourcesList = document.getElementById('resources-list'); const emptyState = document.getElementById('empty-state'); if (!resourcesList || !emptyState) { return; } // 清空列表 resourcesList.innerHTML = ''; // 筛选资源 let filteredResources = Array.from(this.resources.values()); if (this.activeTab !== 'all') { filteredResources = filteredResources.filter(resource => resource.type === this.activeTab); } // 按大小排序(从大到小) filteredResources.sort((a, b) => b.size - a.size); // 显示空状态或资源列表 if (filteredResources.length === 0) { emptyState.style.display = 'flex'; } else { emptyState.style.display = 'none'; // 添加资源项 filteredResources.forEach(resource => { const resourceItem = this.createResourceItem(resource); resourcesList.appendChild(resourceItem); }); } } // 创建资源项 createResourceItem(resource) { const item = document.createElement('li'); item.className = 'resource-item'; // 资源头部 const header = document.createElement('div'); header.className = 'resource-header'; header.innerHTML = `
${this.truncateText(resource.name, 30)}
${this.formatSize(resource.size)}
${this.config.resourceTypes[resource.type].icon} ${resource.type}
`; // 点击展开/折叠 header.addEventListener('click', () => { const content = item.querySelector('.resource-content'); if (content) { content.style.display = content.style.display === 'block' ? 'none' : 'block'; } }); // 资源内容 const content = document.createElement('div'); content.className = 'resource-content'; // 资源预览 const previewContainer = document.createElement('div'); previewContainer.className = 'resource-preview-container'; // 根据资源类型创建预览 if (resource.type === 'image') { const img = document.createElement('img'); img.className = 'resource-thumbnail'; img.src = resource.url; img.alt = resource.name; previewContainer.appendChild(img); } else if (resource.type === 'video') { const video = document.createElement('video'); video.className = 'resource-thumbnail'; video.controls = true; video.src = resource.url; previewContainer.appendChild(video); } else if (resource.type === 'audio') { const audio = document.createElement('audio'); audio.controls = true; audio.src = resource.url; previewContainer.appendChild(audio); } else { // 其他类型的资源,显示图标 previewContainer.innerHTML = `
${this.config.resourceTypes[resource.type].icon}
${resource.type.toUpperCase()}
`; } // 资源操作 const actions = document.createElement('div'); actions.className = 'resource-actions'; actions.innerHTML = ` `; // 添加操作事件 actions.querySelector('.preview').addEventListener('click', (e) => { e.stopPropagation(); this.previewResource(resource); }); actions.querySelector('.download').addEventListener('click', (e) => { e.stopPropagation(); this.downloadResource(resource); }); // 资源URL const url = document.createElement('div'); url.className = 'resource-url'; url.textContent = resource.url; // 组装资源项 content.appendChild(previewContainer); content.appendChild(actions); content.appendChild(url); item.appendChild(header); item.appendChild(content); return item; } // 预览资源 previewResource(resource) { // 创建预览模态框 if (!this.previewModal) { this.createPreviewModal(); } // 设置预览内容 const previewBody = document.querySelector('#preview-modal .preview-body'); const previewTitle = document.querySelector('#preview-modal .preview-title'); if (previewBody && previewTitle) { previewTitle.textContent = resource.name; // 根据资源类型创建预览内容 if (resource.type === 'image') { previewBody.innerHTML = ` ${resource.name} `; } else if (resource.type === 'video') { previewBody.innerHTML = ` `; } else if (resource.type === 'audio') { previewBody.innerHTML = ` `; } else { // 其他类型资源,显示信息和下载按钮 previewBody.innerHTML = `
${this.config.resourceTypes[resource.type].icon}

${resource.name}

类型: ${resource.type}

大小: ${this.formatSize(resource.size)}

`; // 添加下载事件 previewBody.querySelector('.download').addEventListener('click', () => { this.downloadResource(resource); }); } } // 显示预览模态框 this.previewModal.style.display = 'flex'; } // 创建预览模态框 createPreviewModal() { if (this.previewModal) { return; } this.previewModal = document.createElement('div'); this.previewModal.id = 'preview-modal'; this.previewModal.innerHTML = ` `; // 添加关闭事件 this.previewModal.querySelector('.close-btn').addEventListener('click', () => { this.previewModal.style.display = 'none'; }); // 点击模态框外部关闭 this.previewModal.addEventListener('click', (e) => { if (e.target === this.previewModal) { this.previewModal.style.display = 'none'; } }); // 添加到页面 document.body.appendChild(this.previewModal); } // 下载资源 downloadResource(resource) { try { // 使用GM_download下载 GM_download({ url: resource.url, name: resource.name, saveAs: true }); console.log(`正在下载资源: ${resource.name}`); } catch (error) { console.error(`下载资源失败: ${resource.name}`, error); // 降级方案:创建a标签下载 const a = document.createElement('a'); a.href = resource.url; a.download = resource.name; document.body.appendChild(a); a.click(); document.body.removeChild(a); } } // 拦截请求 interceptRequests() { // 保存原始fetch和XMLHttpRequest const originalFetch = window.fetch; const originalXhrOpen = XMLHttpRequest.prototype.open; // 重写fetch window.fetch = async (url, options) => { // 处理请求 this.handleRequest(url); // 执行原始fetch return originalFetch.apply(this, arguments); }; // 重写XMLHttpRequest.open XMLHttpRequest.prototype.open = function(method, url) { // 处理请求 this._url = url; this.addEventListener('load', () => { if (this.status >= 200 && this.status < 300) { // 尝试获取响应大小 const size = this.getResponseHeader('Content-Length') || 0; this.handleRequest(this._url, parseInt(size)); } }); // 执行原始open originalXhrOpen.apply(this, arguments); }; } // 处理请求 handleRequest(url, size = 0) { // 跳过本身的请求 if (url.includes('resource-sniffer')) { return; } // 针对特定网站的视频URL处理 const videoUrl = this.extractVideoUrl(url); if (videoUrl) { // 处理提取到的视频URL const resourceInfo = this.getResourceInfo(videoUrl, size); if (resourceInfo) { // 添加到资源列表 this.addResource(resourceInfo); } return; } // 检查是否为有效的资源URL const resourceInfo = this.getResourceInfo(url, size); if (resourceInfo) { // 添加到资源列表 this.addResource(resourceInfo); } } // 提取视频URL(针对特定网站) extractVideoUrl(url) { // 西瓜视频 if (url.includes('ixigua.com')) { // 处理西瓜视频API请求 if (url.includes('ixigua.com/api/albumv2/') || url.includes('ixigua.com/api/videov2/pseries_more_v2') || url.includes('ixigua.com/api/mixVideo/')) { return url; } // 普通西瓜视频URL return url; } // 抖音 if (url.includes('douyin.com')) { return url; } // YouTube if (url.includes('youtube.com') || url.includes('youtu.be')) { return url; } // 央视网 if (url.includes('cntv') && url.includes('/asp/')) { // 央视网URL特殊处理 const realUrl = url.replace(/.+?cntv.*?\/asp\/.*?hls\/(.*)/, 'https://hls.cntv.myalicdn.com/asp/hls/$1'); return realUrl; } // 检查是否为常见视频格式URL(即使没有扩展名) if (url.includes('.m3u8') || url.includes('.mp4') || url.includes('.webm') || url.includes('.flv') || url.includes('.avi') || url.includes('.mov') || url.includes('.f4v') || url.includes('.mkv') || url.includes('.rmvb') || url.includes('.wmv') || url.includes('.3gp')) { return url; } // 处理没有扩展名但可能是视频的URL if (url.includes('video') || url.includes('stream') || url.includes('media') || url.includes('play') || url.includes('source') || url.includes('file')) { // 检查是否为PHP请求但没有明显视频扩展名 if (url.includes('.php') && !url.includes('.jpg') && !url.includes('.png') && !url.includes('.gif') && !url.includes('.css') && !url.includes('.js')) { return url + '&type=.m3u8'; // 尝试添加m3u8格式参数 } return url; } return null; } // 获取资源信息 getResourceInfo(url, size = 0) { try { const parsedUrl = new URL(url); const pathname = parsedUrl.pathname; const filename = pathname.split('/').pop() || 'unknown'; let extension = filename.split('.').pop().toLowerCase(); let siteInfo = this.getSiteInfo(url); // 确定资源类型 let type = 'other'; // 特殊处理没有扩展名但可能是视频的URL if (extension === filename) { // 检查是否为视频URL if (url.includes('.m3u8') || url.includes('video') || url.includes('stream') || url.includes('media') || url.includes('play') || url.includes('source')) { type = 'video'; extension = 'mp4'; // 假设默认视频格式 } } else { // 根据扩展名确定资源类型 for (const [resourceType, config] of Object.entries(this.config.resourceTypes)) { if (config.extensions && config.extensions.includes(extension)) { type = resourceType; break; } } } // 额外检查:如果URL包含视频相关关键词但类型不是视频 if (type !== 'video' && ( url.includes('video') || url.includes('stream') || url.includes('media') || url.includes('.m3u8') || url.includes('play') || url.includes('source'))) { type = 'video'; } // 网站特定处理 let headers = {}; if (siteInfo === 'ixigua') { headers.Referer = 'https://www.ixigua.com/'; } else if (siteInfo === 'douyin') { headers.Referer = 'https://www.douyin.com/'; } else if (siteInfo === 'cntv') { // 央视网特定处理 if (url.includes('.m3u8')) { // 处理不同分辨率 if (url.includes('main.m3u8')) { // 提供多种分辨率选项 const url720p = url.replace(/main.m3u8.*/, '1200.m3u8').replace('hls/main/', 'hls/1200/'); const url1080p = url.replace(/main.m3u8.*/, '2000.m3u8').replace('hls/main/', 'hls/2000/'); // 这里可以返回多个分辨率的视频 } } } else if (siteInfo === 'javplayer') { headers.Referer = 'https://javplayer.me/'; } else if (siteInfo === 'aliyundrive') { headers.Referer = 'https://www.aliyundrive.com/'; } // 添加User-Agent以提高兼容性 headers['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'; headers['Accept-Language'] = 'zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7'; // 忽略小资源 if (this.config.ignoreSmallResources && size > 0 && size < this.config.minResourceSize) { // 视频资源即使小也不忽略 if (type !== 'video') { return null; } } return { id: url, url: url, name: filename, extension: extension, type: type, size: size, timestamp: Date.now(), // 添加网站特定信息 site: siteInfo, // 添加请求头信息 headers: headers }; } catch (error) { console.error('解析URL失败:', error); return null; } } // 获取网站信息 getSiteInfo(url) { if (url.includes('ixigua.com')) return 'ixigua'; if (url.includes('douyin.com')) return 'douyin'; if (url.includes('youtube.com') || url.includes('youtu.be')) return 'youtube'; if (url.includes('bilibili.com')) return 'bilibili'; if (url.includes('cntv')) return 'cntv'; if (url.includes('javplayer.me')) return 'javplayer'; if (url.includes('aliyundrive.com')) return 'aliyundrive'; if (url.includes('weibo.cn')) return 'weibo'; return 'other'; } // 添加资源 addResource(resource) { // 检查是否已存在 if (this.resources.has(resource.id)) { return; } // 检查是否超过最大资源数 if (this.resources.size >= this.config.maxResources) { // 删除最早添加的资源 const oldestResource = Array.from(this.resources.entries()).sort((a, b) => a[1].timestamp - b[1].timestamp)[0]; this.resources.delete(oldestResource[0]); } // 添加资源 this.resources.set(resource.id, resource); // 更新UI this.updateUI(); } // 监听媒体元素 monitorMediaElements() { // 定期检查新的媒体元素 setInterval(() => { // 检查图片 document.querySelectorAll('img:not([data-sniffed])').forEach(img => { img.dataset.sniffed = 'true'; const url = img.src; this.handleRequest(url); }); // 检查视频 document.querySelectorAll('video:not([data-sniffed])').forEach(video => { video.dataset.sniffed = 'true'; // 检查视频源 video.querySelectorAll('source').forEach(source => { const url = source.src; this.handleRequest(url); }); // 如果视频有直接src if (video.src) { this.handleRequest(video.src); } }); // 检查音频 document.querySelectorAll('audio:not([data-sniffed])').forEach(audio => { audio.dataset.sniffed = 'true'; // 检查音频源 audio.querySelectorAll('source').forEach(source => { const url = source.src; this.handleRequest(url); }); // 如果音频有直接src if (audio.src) { this.handleRequest(audio.src); } }); }, 2000); } // 格式化大小 formatSize(bytes) { if (bytes === 0) return '0 Bytes'; const k = 1024; const sizes = ['Bytes', 'KB', 'MB', 'GB']; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; } // 截断文本 truncateText(text, maxLength) { if (text.length <= maxLength) return text; return text.substring(0, maxLength) + '...'; } } // 初始化资源嗅探器 window.addEventListener('load', () => { setTimeout(() => { const sniffer = new ResourceSniffer(); // 为了测试,直接显示面板 sniffer.panelVisible = true; sniffer.createPanel(); sniffer.panelElement.style.display = 'flex'; }, 1000); });