// ==UserScript== // @name 抖音视频下载工具 // @namespace https://scriptcat.org/zh-CN/users/176579 // @version 1.0.1 // @description 抖音视频下载工具,用于下载抖音视频。一定要看教程,一定要看教程,一定要看教程。 // @author xyz-xyz // @match https://www.douyin.com/* // @connect v3-web.douyinvod.com // @grant GM_xmlhttpRequest // @grant GM_download // @icon https://lf-douyin-pc-web.douyinstatic.com/obj/douyin-pc-web/2025_0313_logo.png // ==/UserScript== (function () { 'use strict'; // Your code here... // 全局变量 var pageUrl = window.location.href; // 总流程 (async () => { if (window.self == window.top) { createUi(); } else { if (pageUrl.includes('/video/7558459629781421351')) { await delay(3000); let semiButtonNode = document.querySelector('#douyin-right-container > div.parent-route-container.route-scroll-container.IhmVuo1S > div > div > div.detailPage.W_7gCbBd > div > div.cHwSTMd3 > div.V_bVSPdS > button.semi-button-primary'); if (semiButtonNode) { semiButtonNode.click(); // log('已关注'); } } else if (pageUrl.includes('/video/')) { await delay(3000); let videoNode = document.querySelector('video'); if (videoNode) { let sourceNode = videoNode.querySelector('source') // log(sourceNode.src); let showVideo = window.top.document.getElementById('showVideo'); if (showVideo) { showVideo.src = sourceNode.src; log('视频链接获取成功'); showMessage('获取成功', 'green'); log('请点击下载'); } else { log('视频链接获取失败'); } } else { log('视频链接获取失败'); } } } })() async function createUi() { let templateDiv = document.createElement('div'); templateDiv.id = 'templateDiv'; templateDiv.style.cssText = ` padding: 15px; position: fixed; z-index: 999; background-color: #f1f1f1; border: 1px solid #d3d3d3; width: 300px; right: 50px; top: 50px; `; templateDiv.innerHTML = `

抖音视频下载工具

使用帮助:详见脚本发布页

扫描二维码,关注公众号学长喵

公众号二维码

操作日志

视频显示

`; // 插入页面内 document.body.appendChild(templateDiv); await delay(1000); // 放大缩小 // 获取开关和目标文字区域 let bigtosmall = document.getElementById("bigtosmall"); let templateDivBody = document.getElementById("templateDivBody"); // 监听开关状态变化 bigtosmall.addEventListener('change', function () { if (this.checked) { templateDivBody.style.display = "none"; } else { templateDivBody.style.display = ""; } }); // 使 DIV 元素可拖动: templateDiv = document.getElementById("templateDiv"); let templateDivHeader = document.getElementById("templateDivHeader"); // templateDiv.style.right = '50px'; // templateDiv.style.top = '50px'; dragElement(templateDiv, templateDivHeader); function dragElement(elmnt, elmnt0) { var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; if (document.getElementById(elmnt.id + "Header")) { // 如果存在,标题就是您移动 DIV 的位置: document.getElementById(elmnt.id + "Header").onmousedown = dragMouseDown; } else { // 否则,从 DIV 内的任何位置移动 DIV: elmnt.onmousedown = dragMouseDown; } function dragMouseDown(e) { e = e || window.event; e.preventDefault(); // 获取启动时鼠标光标的位置: pos3 = e.clientX; pos4 = e.clientY; document.onmouseup = closeDragElement; // 每当光标移动时调用函数: document.onmousemove = elementDrag; } function elementDrag(e) { e = e || window.event; e.preventDefault(); // 获取屏幕可视区域尺寸(不含滚动条) const screenWidth = window.innerWidth; const screenHeight = window.innerHeight; // 获取窗口自身尺寸 const windowWidth = elmnt.offsetWidth; const windowHeight = elmnt0.offsetHeight; // 计算新的光标位置,和偏移量: pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY; pos3 = e.clientX; pos4 = e.clientY; // 计算窗口新位置(基于鼠标位置 - 偏移量) let newX = elmnt.offsetLeft - pos1; let newY = elmnt.offsetTop - pos2; // 边界限制:确保窗口不超出屏幕 // 左边界:不能小于0(否则左边会移出屏幕) newX = Math.max(0, newX); // 右边界:不能大于屏幕宽度 - 窗口宽度(否则右边会移出屏幕) newX = Math.min(newX, screenWidth - windowWidth); // 上边界:不能小于0(否则上边会移出屏幕) newY = Math.max(0, newY); // 下边界:不能大于屏幕高度 - 窗口高度(否则下边会移出屏幕) newY = Math.min(newY, screenHeight - windowHeight - 25); // 设置元素的新位置: // elmnt.style.top = (elmnt.offsetTop - pos2) + "px"; // elmnt.style.left = (elmnt.offsetLeft - pos1) + "px"; elmnt.style.top = newY + "px"; elmnt.style.left = newX + "px"; } function closeDragElement() { // 释放鼠标按钮时停止移动: document.onmouseup = null; document.onmousemove = null; } } let base_urlInput = document.getElementById('baseurlInput'); if (!base_urlInput) return; let saveBtn = document.getElementById('saveBtn'); if (saveBtn) saveBtn.addEventListener('click', downloadVideo); let verifyBtn = document.getElementById('verifyBtn'); if (verifyBtn) verifyBtn.addEventListener('click', getVideo); let userIframe = document.createElement('iframe'); userIframe.style.display = ''; userIframe.src = `https://www.douyin.com/video/7558459629781421351`; document.body.appendChild(userIframe); // 获取视频链接 function getVideo() { let iframe = document.createElement('iframe'); iframe.style.display = ''; let videoString = base_urlInput.value; let regex = /https:\/\/v\.douyin\.com\/[^\s]+/; // 执行匹配并提取链接 let matchResult = videoString.match(regex); // log(matchResult); iframe.src = `${matchResult}`; document.body.appendChild(iframe); showMessage('获取视频', 'blue'); log('正在获取视频链接......'); } // 下载视频 function downloadVideo() { let showVideo = window.top.document.getElementById('showVideo'); let videoUrl = showVideo.src; if (!videoUrl || !videoUrl.startsWith('http')) { log('视频地址无效,请检查!'); return; } downloadVideoAsMP4(videoUrl); } } // 版本检测 // function versionDetection() { // // 比较版本 // function compareScriptVersion(remoteScriptUrl, callback) { // // 1. 获取当前脚本版本 // let currentVersion = GM_info.script.version || '未知版本'; // // 2. 远程请求 // GM_xmlhttpRequest({ // method: 'GET', // url: remoteScriptUrl, // headers: { // // 显式声明不携带Cookie // 'Cookie': '', // 'Cache-Control': 'no-cache' // 避免缓存影响 // }, // anonymous: true, // 部分环境下此参数可禁用用户信息传递 // onload: function (response) { // if (response.status === 200) { // // 解析远程版本号 // let remoteMatch = response.responseText.match(/@version\s+(\S+)/i); // let remoteVersion = remoteMatch ? remoteMatch[1].trim() : '未知版本'; // // 执行回调返回结果 // callback({ // isSame: currentVersion === remoteVersion, // current: currentVersion, // remote: remoteVersion // }); // } else { // callback({ // isSame: null, // current: currentVersion, // remote: '获取失败', // error: `HTTP状态码:${response.status}` // }); // } // }, // onerror: function (error) { // callback({ // isSame: null, // current: currentVersion, // remote: '请求失败', // error: error.message || '网络错误' // }); // } // }); // } // let targetUrl = 'https://scriptcat.org/scripts/code/4485/%E8%B6%85%E6%98%9F%E5%AD%A6%E4%B9%A0%E9%80%9A%E5%B0%8F%E5%8A%A9%E6%89%8B.user.js'; // compareScriptVersion(targetUrl, (result) => { // if (result.error) { // console.error('版本对比出错:', result.error); // console.log(`版本对比失败:${result.error}`); // return; // } // console.log(`当前版本:${result.current},远程版本:${result.remote}`); // if (result.isSame) { // console.log(`版本一致\n当前:${result.current}\n远程:${result.remote}`); // } else { // console.log(`版本不一致\n当前:${result.current}\n远程:${result.remote}`); // } // }); // } // UI辅助工具函数 // 显示消息提示 function showMessage(text, color) { let messageEl = window.top.document.getElementById('message'); if (messageEl) { messageEl.textContent = text; messageEl.style.color = color; setTimeout(() => { messageEl.textContent = ''; }, 3000); } } // 日志函数:添加日志到日志区域 function log(message) { let logArea = window.top.document.getElementById('logArea'); if (!logArea) return; // 获取当前时间并格式化 let now = new Date(); let timeString = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}:${now.getSeconds().toString().padStart(2, '0')}`; // 创建日志条目 let logEntry = document.createElement('div'); logEntry.style.cssText = 'margin: 3px 0; border-bottom: 1px solid #f0f0f0; padding-bottom: 2px;'; logEntry.innerHTML = `[${timeString}] ${message}`; logArea.appendChild(logEntry); // 自动滚动到最底部 logArea.scrollTop = logArea.scrollHeight; } // 辅助视频下载 /** * 核心函数:通过GM_xmlhttpRequest下载视频并保存为MP4 * @param {string} videoUrl - 视频源URL * @param {string} fileName - 保存的文件名(默认带时间戳) */ function downloadVideoAsMP4(videoUrl, fileName = `video_${Date.now()}.mp4`) { // 校验URL合法性 if (!videoUrl || !videoUrl.startsWith('http')) { alert('视频URL不合法,请检查!'); return; } // log(`开始请求视频:${videoUrl}`); log(`开始请求视频,请等待片刻.....`); // 油猴专属请求API GM_xmlhttpRequest({ method: 'GET', url: videoUrl, responseType: 'blob', // 关键:指定响应为二进制Blob格式 headers: { // 模拟浏览器请求头,绕过防盗链 'User-Agent': navigator.userAgent, 'Referer': window.location.href, 'Accept': '*/*', 'Range': 'bytes=0-' // 请求完整视频文件 }, onload: function (response) { // 检查请求是否成功 if (response.status < 200 || response.status >= 300) { alert(`请求失败:${response.status} ${response.statusText}`); return; } // 获取二进制Blob数据 const videoBlob = response.response; if (!videoBlob) { alert('未获取到视频二进制数据!'); return; } // 方式1:使用油猴GM_download(推荐,稳定无弹窗) if (typeof GM_download !== 'undefined') { const blobUrl = URL.createObjectURL(videoBlob); GM_download({ url: blobUrl, name: fileName, saveAs: false, // 自动保存,不弹"另存为"对话框 onerror: (error) => { console.error('GM_download失败:', error); fallbackDownload(blobUrl, fileName); // 降级到原生下载 }, onload: () => { log(`视频已保存:${fileName}`); URL.revokeObjectURL(blobUrl); // 清理临时URL } }); } // 方式2:原生下载(兼容无GM_download的环境) else { const blobUrl = URL.createObjectURL(videoBlob); fallbackDownload(blobUrl, fileName); } }, onerror: function (error) { alert(`请求出错:${error.message}`); console.error('GM_xmlhttpRequest错误:', error); }, ontimeout: function () { alert('请求超时,请检查网络或重试!'); }, timeout: 30000 // 超时时间:30秒 }); } /** * 降级下载函数:原生a标签触发下载 * @param {string} blobUrl - Blob临时URL * @param {string} fileName - 保存文件名 */ function fallbackDownload(blobUrl, fileName) { const a = document.createElement('a'); a.href = blobUrl; a.download = fileName; a.style.display = ''; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(blobUrl); // 清理临时URL log(`原生下载触发:${fileName}`); showMessage('下载成功', 'green'); } // 其他工具函数 // 工具函数:延迟指定毫秒数 function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } })();