// ==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 = `
`;
} 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);
});