您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
高级视频&音频解析器,支持抖音无水印和流媒体格式
当前为
// ==UserScript== // @name TypeMonkey视频&音频解析器 // @namespace https://github.com/yourname // @version 2.5 // @description 高级视频&音频解析器,支持抖音无水印和流媒体格式 // @author YourName // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_download // @grant GM_notification // @grant GM_setClipboard // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @connect * // @run-at document-idle // ==/UserScript== (function() { 'use strict'; // 抖音无水印解析API const DOUYIN_API = "https://api.douyin.wtf/api?url="; // 全局变量 let floatBall = null; let panel = null; let lastPosition = { x: GM_getValue('lastPositionX', 20), y: GM_getValue('lastPositionY', 20) }; let isExpanded = false; let sourcesCount = 0; // 添加全局样式 GM_addStyle(` #videoParserFloatBall { position: fixed; width: 50px; height: 50px; background: linear-gradient(145deg, #4a76c6, #3a66b6); border-radius: 50%; cursor: pointer; z-index: 99999; display: flex; justify-content: center; align-items: center; color: white; font-size: 24px; box-shadow: 0 6px 15px rgba(0,0,0,0.3); transition: all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55); user-select: none; } #videoParserFloatBall.badge::after { content: ''; position: absolute; top: -5px; right: -5px; width: 15px; height: 15px; background: #ff4757; border-radius: 50%; border: 2px solid white; } #videoParserPanel { position: fixed; z-index: 99998; background: #2d3a4b; color: #ecf0f1; padding: 0; border-radius: 15px; box-shadow: 0 12px 30px rgba(0,0,0,0.4); width: 95%; max-width: 420px; max-height: 85vh; overflow: hidden; font-family: system-ui, -apple-system, sans-serif; display: flex; flex-direction: column; display: none; transform-origin: top center; font-size: 14px; border: 1px solid #3a4a60; } #videoParserPanel .header { display: flex; justify-content: space-between; align-items: center; padding: 15px; background: #1e2a38; color: white; cursor: move; border-bottom: 1px solid #3a4a60; user-select: none; } #videoParserPanel .header h3 { margin: 0; font-size: 16px; font-weight: 600; color: #3498db; } #videoParserPanel .header .buttons { display: flex; gap: 10px; } #videoParserPanel .header button { width: 30px; height: 30px; background: #3a4a60; color: white; border: none; border-radius: 6px; cursor: pointer; display: flex; justify-content: center; align-items: center; font-size: 18px; transition: background 0.3s; } #videoParserPanel .header button:hover { background: #4a5a70; } #videoParserPanel .header .close:hover { background: #ff4757; } #panelContent { overflow-y: auto; padding: 15px; max-height: calc(85vh - 60px); background: #1e2a38; font-size: 14px; } .source-item { margin: 15px 0; padding: 15px; background: #2d3a4b; border-radius: 10px; box-shadow: 0 4px 10px rgba(0,0,0,0.2); border: 1px solid #3a4a60; } .source-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; } .source-info { font-weight: bold; display: flex; align-items: center; gap: 10px; } .source-url { word-break: break-all; font-size: 13px; color: #bdc3c7; padding: 12px; background: #1a2432; border-radius: 8px; max-height: 80px; overflow-y: auto; margin-bottom: 15px; border: 1px solid #2d3a4b; } .btn-group { display: flex; gap: 10px; flex-wrap: wrap; } .btn-group button { padding: 8px 15px; color: white; border: none; border-radius: 8px; cursor: pointer; flex: 1; font-size: 13px; min-width: 100px; font-weight: 600; transition: all 0.3s; } .btn-group button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.2); } .copy-btn { background: linear-gradient(145deg, #2ecc71, #27ae60); } .douyin-btn { background: linear-gradient(145deg, #e74c3c, #c0392b); } .download-btn { background: linear-gradient(145deg, #9b59b6, #8e44ad); } .audio-btn { background: linear-gradient(145deg, #f39c12, #d35400); } .empty-msg { text-align: center; padding: 40px 20px; color: #7f8c8d; } .empty-msg p { margin: 10px 0; } @media (max-width: 500px) { #videoParserPanel { width: 98%; max-width: none; left: 1% !important; right: 1% !important; transform: none !important; max-width: unset; } .btn-group button { min-width: 45%; } } `); // 创建球形悬浮按钮 function createFloatBall() { if (floatBall) return; floatBall = document.createElement('div'); floatBall.id = 'videoParserFloatBall'; floatBall.textContent = '🎥'; floatBall.title = '视频&音频解析器'; // 定位 floatBall.style.left = `${lastPosition.x}px`; floatBall.style.top = `${lastPosition.y}px`; // 添加拖拽功能 let isDragging = false; let offsetX, offsetY; floatBall.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); function startDrag(e) { isDragging = true; offsetX = e.clientX - floatBall.getBoundingClientRect().left; offsetY = e.clientY - floatBall.getBoundingClientRect().top; floatBall.style.cursor = 'grabbing'; floatBall.style.boxShadow = '0 10px 25px rgba(0,0,0,0.4)'; } function drag(e) { if (!isDragging) return; floatBall.style.left = `${e.clientX - offsetX}px`; floatBall.style.top = `${e.clientY - offsetY}px`; lastPosition = { x: e.clientX - offsetX, y: e.clientY - offsetY }; GM_setValue('lastPositionX', lastPosition.x); GM_setValue('lastPositionY', lastPosition.y); } function stopDrag() { isDragging = false; floatBall.style.cursor = 'pointer'; floatBall.style.boxShadow = '0 6px 15px rgba(0,0,0,0.3)'; } // 点击展开面板 floatBall.addEventListener('click', async () => { if (isExpanded) { collapsePanel(); } else { const sources = await findVideoSources(); showResults(sources); } }); document.body.appendChild(floatBall); } // 展开面板 function expandPanel() { if (!panel) return; isExpanded = true; floatBall.style.display = 'none'; // 设置面板初始位置 const rect = floatBall.getBoundingClientRect(); panel.style.left = `${rect.left}px`; panel.style.top = `${rect.top}px`; panel.style.display = 'flex'; panel.style.transform = 'scale(0.5) translateY(-30px)'; panel.style.opacity = '0'; // 动画效果 setTimeout(() => { panel.style.transition = 'all 0.5s cubic-bezier(0.68, -0.55, 0.265, 1.55)'; panel.style.transform = 'scale(1) translateY(0)'; panel.style.opacity = '1'; }, 10); } // 收起面板 function collapsePanel() { if (!panel || !floatBall) return; isExpanded = false; // 动画效果 panel.style.transition = 'all 0.4s cubic-bezier(0.68, -0.55, 0.265, 1.55)'; panel.style.transform = 'scale(0.5) translateY(-30px)'; panel.style.opacity = '0'; setTimeout(() => { panel.style.display = 'none'; floatBall.style.display = 'flex'; floatBall.style.left = `${lastPosition.x}px`; floatBall.style.top = `${lastPosition.y}px`; }, 400); } // 主解析函数 async function findVideoSources() { const sources = []; const now = Date.now(); const uniqueSources = new Set(); // 1. 解析标准视频元素 document.querySelectorAll('video').forEach(video => { if (video.src) addUniqueSource(sources, uniqueSources, 'video', video.src); video.querySelectorAll('source').forEach(s => { if (s.src) addUniqueSource(sources, uniqueSources, 'source', s.src); }); }); // 2. 解析音频元素 document.querySelectorAll('audio').forEach(audio => { if (audio.src) addUniqueSource(sources, uniqueSources, 'audio', audio.src); audio.querySelectorAll('source').forEach(s => { if (s.src) addUniqueSource(sources, uniqueSources, 'audio-source', s.src); }); }); // 3. 解析iframe嵌入视频 const iframePromises = []; document.querySelectorAll('iframe').forEach(iframe => { const src = iframe.src || iframe.dataset.src; if (!src) return; // 抖音特殊处理 if (src.includes("douyin.com") || src.includes("iesdouyin.com")) { iframePromises.push( parseDouyin(src).then(url => { if (url) addUniqueSource(sources, uniqueSources, 'douyin', url); }) ); } else if (/youtube|vimeo|bilibili/.test(src)) { addUniqueSource(sources, uniqueSources, 'iframe', src); } }); // 等待所有抖音解析完成 await Promise.all(iframePromises); // 更新计数 sourcesCount = sources.length; if (sourcesCount > 0 && !isExpanded) { floatBall.classList.add('badge'); } return sources; } // 添加唯一来源 function addUniqueSource(sources, uniqueSet, type, url) { if (!url || uniqueSet.has(url)) return; uniqueSet.add(url); sources.push(createSource(type, url)); } // 创建来源对象 function createSource(type, url) { return { type: type, url: url, timestamp: Date.now(), isStream: /\.(m3u8|mpd)(\?|$)/i.test(url), isAudio: /audio|audio-source/.test(type) }; } // 抖音无水印解析 function parseDouyin(url) { return new Promise(resolve => { GM_xmlhttpRequest({ method: "GET", url: `${DOUYIN_API}${encodeURIComponent(url)}`, timeout: 8000, onload: function(response) { try { const data = JSON.parse(response.responseText); resolve(data.nwm_video_url || null); } catch (e) { resolve(null); } }, onerror: function() { resolve(null); }, ontimeout: function() { resolve(null); } }); }); } // 显示结果面板 function showResults(sources) { if (panel) panel.remove(); panel = document.createElement('div'); panel.id = 'videoParserPanel'; // 标题栏 const header = document.createElement('div'); header.className = 'header'; const title = document.createElement('h3'); title.textContent = sources.length > 0 ? `🎥 发现 ${sources.length} 个媒体源` : `⚠️ 未检测到媒体源`; const buttonContainer = document.createElement('div'); buttonContainer.className = 'buttons'; // 刷新按钮 const refreshBtn = document.createElement('button'); refreshBtn.textContent = '↻'; refreshBtn.title = '重新扫描'; // 收起按钮 const minimizeBtn = document.createElement('button'); minimizeBtn.textContent = '−'; minimizeBtn.title = '收起/展开'; // 关闭按钮 const closeBtn = document.createElement('button'); closeBtn.textContent = '×'; closeBtn.title = '关闭'; closeBtn.className = 'close'; buttonContainer.appendChild(refreshBtn); buttonContainer.appendChild(minimizeBtn); buttonContainer.appendChild(closeBtn); header.appendChild(title); header.appendChild(buttonContainer); // 内容区域 const content = document.createElement('div'); content.id = 'panelContent'; if (sources.length > 0) { const list = document.createElement('div'); sources.forEach((src, i) => { const item = document.createElement('div'); item.className = 'source-item'; const headerRow = document.createElement('div'); headerRow.className = 'source-header'; const sourceInfo = document.createElement('div'); sourceInfo.className = 'source-info'; const sourceType = document.createElement('span'); let typeText = src.type.toUpperCase(); if (src.isAudio) { sourceType.style.color = '#f39c12'; typeText = '音频资源'; } else if (src.type === 'douyin') { sourceType.style.color = '#e74c3c'; } else if (src.type === 'stream') { sourceType.style.color = '#9b59b6'; } else { sourceType.style.color = '#3498db'; } sourceType.textContent = `[${typeText}]`; const sourceIndex = document.createElement('span'); sourceIndex.textContent = `#${i+1}`; sourceInfo.appendChild(sourceIndex); sourceInfo.appendChild(sourceType); const urlText = document.createElement('div'); urlText.className = 'source-url'; urlText.textContent = src.url; const btnGroup = document.createElement('div'); btnGroup.className = 'btn-group'; // 复制按钮 const copyBtn = document.createElement('button'); copyBtn.textContent = '复制链接'; copyBtn.className = 'copy-btn'; copyBtn.dataset.url = src.url; // 下载按钮 let downloadBtn = null; if (src.isAudio) { downloadBtn = document.createElement('button'); downloadBtn.textContent = '下载音频'; downloadBtn.className = 'audio-btn'; downloadBtn.dataset.download = src.url; } else if (src.type === 'douyin') { downloadBtn = document.createElement('button'); downloadBtn.textContent = '无水印下载'; downloadBtn.className = 'douyin-btn'; downloadBtn.dataset.download = src.url; } else if (!src.isStream) { downloadBtn = document.createElement('button'); downloadBtn.textContent = '下载视频'; downloadBtn.className = 'download-btn'; downloadBtn.dataset.download = src.url; } btnGroup.appendChild(copyBtn); if (downloadBtn) btnGroup.appendChild(downloadBtn); item.appendChild(headerRow); item.appendChild(urlText); item.appendChild(btnGroup); headerRow.appendChild(sourceInfo); list.appendChild(item); }); content.appendChild(list); } else { const emptyMsg = document.createElement('div'); emptyMsg.className = 'empty-msg'; emptyMsg.innerHTML = ` 📹 未找到视频或音频资源 尝试播放媒体后重新扫描 `; content.appendChild(emptyMsg); } // 组装面板 panel.appendChild(header); panel.appendChild(content); document.body.appendChild(panel); // 添加事件监听器 function setupEventListeners() { // 复制功能 content.querySelectorAll('.copy-btn').forEach(btn => { btn.addEventListener('click', () => { GM_setClipboard(btn.dataset.url); GM_notification({ title: '复制成功', text: '链接已复制到剪贴板', timeout: 2000 }); }); }); // 下载功能 content.querySelectorAll('.download-btn, .audio-btn, .douyin-btn').forEach(btn => { btn.addEventListener('click', () => { const url = btn.dataset.download; const isAudio = btn.classList.contains('audio-btn'); let fileExtension = 'mp4'; if (isAudio) { fileExtension = url.includes('.m4a') ? 'm4a' : url.includes('.aac') ? 'aac' : url.includes('.wav') ? 'wav' : 'mp3'; } const filename = `${isAudio ? 'audio' : 'video'}_${Date.now()}.${fileExtension}`; GM_download({ url: url, name: filename }); }); }); // 刷新功能 refreshBtn.addEventListener('click', async () => { refreshBtn.textContent = '...'; refreshBtn.disabled = true; const newSources = await findVideoSources(); showResults(newSources); }); // 收起功能 minimizeBtn.addEventListener('click', collapsePanel); // 关闭功能 closeBtn.addEventListener('click', () => { if (panel) panel.remove(); if (floatBall) floatBall.remove(); panel = null; floatBall = null; }); // 拖动功能 let isDragging = false; let offsetX, offsetY; header.addEventListener('mousedown', startDrag); document.addEventListener('mousemove', drag); document.addEventListener('mouseup', stopDrag); function startDrag(e) { if (e.target.tagName === 'BUTTON') return; isDragging = true; offsetX = e.clientX - panel.getBoundingClientRect().left; offsetY = e.clientY - panel.getBoundingClientRect().top; panel.style.cursor = 'grabbing'; } function drag(e) { if (!isDragging) return; panel.style.left = `${e.clientX - offsetX}px`; panel.style.top = `${e.clientY - offsetY}px`; } function stopDrag() { isDragging = false; panel.style.cursor = 'default'; } } setupEventListeners(); expandPanel(); } // 主执行函数 function init() { createFloatBall(); // 初始扫描 setTimeout(async () => { await findVideoSources(); }, 5000); } // 等待页面加载完成后初始化 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();