// ==UserScript==
// @name 抖音视频下载工具
// @namespace https://scriptcat.org/zh-CN/users/176579
// @version 1.1.0
// @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';
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();
}
} else if (pageUrl.includes('/video/')) {
await delay(3000);
let videoNode = document.querySelector('video');
if (videoNode) {
let sourceNode = videoNode.querySelector('source:nth-child(3)');
let showVideo = window.top.document.getElementById('showVideo');
if (showVideo) {
showVideo.src = sourceNode.src;
log('视频链接获取成功');
showMessage('获取成功', 'green');
log('请点击下载');
} else {
log('视频链接获取失败,2');
}
} else {
log('视频链接获取失败,1');
}
}
}
})()
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 = "";
}
});
templateDiv = document.getElementById("templateDiv");
let templateDivHeader = document.getElementById("templateDivHeader");
dragElement(templateDiv, templateDivHeader);
function dragElement(elmnt, elmnt0) {
var pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
if (document.getElementById(elmnt.id + "Header")) {
document.getElementById(elmnt.id + "Header").onmousedown = dragMouseDown;
} else {
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;
newX = Math.max(0, newX);
newX = Math.min(newX, screenWidth - windowWidth);
newY = Math.max(0, newY);
newY = Math.min(newY, screenHeight - windowHeight - 25);
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);
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 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;
}
function downloadVideoAsMP4(videoUrl, fileName = `video_${Date.now()}.mp4`) {
if (!videoUrl || !videoUrl.startsWith('http')) {
alert('视频URL不合法,请检查!');
return;
}
log(`开始请求视频,请等待片刻.....`);
GM_xmlhttpRequest({
method: 'GET',
url: videoUrl,
responseType: '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;
}
const videoBlob = response.response;
if (!videoBlob) {
alert('未获取到视频二进制数据!');
return;
}
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}`);
showMessage('下载成功', 'green');
URL.revokeObjectURL(blobUrl);
}
});
}
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
});
}
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);
log(`原生下载触发:${fileName}`);
showMessage('下载成功', 'green');
}
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
})();