Greasy Fork is available in English.
Download TikTok videos without watermark
当前为
// ==UserScript==
// @name Tiktok Video Downloader🎬
// @namespace http://greasyfork.icu/en/scripts/431826
// @version 2.1
// @description Download TikTok videos without watermark
// @author YAD
// @match *://*.tiktok.com/*
// @grant GM_xmlhttpRequest
// @grant GM_download
// @icon https://miro.medium.com/v2/resize:fit:512/1*KX6NTUUHWlCP4sCXz28TBA.png
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const getFileName = (url) => {
const videoId = url.split('/').pop().split('?')[0];
return `TikTok_Video_${videoId}.mp4`;
};
const downloadVideo = (url, button) => {
if (!url) {
button.textContent = '✖️';
return;
}
button.textContent = '⏳';
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'blob',
onload: ({ response, status, statusText }) => {
if (response) {
GM_download({
url: URL.createObjectURL(response),
name: getFileName(url),
onload: () => {
button.textContent = '✔️';
setTimeout(() => button.remove(), 2000);
},
onerror: () => {
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
}
});
} else {
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
}
},
onerror: (error) => {
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
},
ontimeout: () => {
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
}
});
};
const createDownloadButton = (video) => {
const button = document.createElement('div');
Object.assign(button.style, {
position: 'absolute',
right: '15px',
top: '27%',
transform: 'translateY(-50%)',
width: '50px',
height: '50px',
backgroundColor: '#ff3b5c',
color: '#ffffff',
fontSize: '18px',
textShadow: '3px 3px 0px #9C1331',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '50%',
cursor: 'pointer',
zIndex: '1000'
});
button.textContent = '🎞️';
button.onclick = async (e) => {
e.stopPropagation();
e.preventDefault();
button.textContent = '⏳';
const videoUrl = video.src || video.querySelector('source')?.src;
if (videoUrl && videoUrl.startsWith('blob:')) {
button.style.backgroundColor = '#ffc107';
const xgwrapper = document.querySelector('[id^="xgwrapper-"]');
const videoId = xgwrapper?.id.split('-')[2];
const tiktokVideoUrl = `https://www.tiktok.com/@YAD/video/${videoId}`;
const iframe = document.createElement('iframe');
iframe.style.position = 'fixed';
iframe.style.visibility = 'hidden';
iframe.style.top = '20px';
iframe.style.left = '5px';
iframe.style.border = 'none';
iframe.src = tiktokVideoUrl;
document.body.appendChild(iframe);
const muteVideoInIframe = () => {
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
const videoElement = iframeDocument.querySelector('video');
if (videoElement) {
videoElement.muted = true;
}
};
const checkVideoUrl = () => {
const iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
const videoElement = iframeDocument.querySelector('video');
if (videoElement && !videoElement.src.startsWith('blob:')) {
muteVideoInIframe();
downloadVideo(videoElement.src, button);
iframe.remove();
} else {
setTimeout(checkVideoUrl, 1000);
}
};
setTimeout(checkVideoUrl, 8000);
} else {
downloadVideo(videoUrl, button);
}
};
video.parentNode.style.position = 'relative';
video.parentNode.appendChild(button);
return button;
};
const manageDownloadButtons = (video) => {
let button;
video.addEventListener('mouseover', () => {
if (!button) {
button = createDownloadButton(video);
}
});
video.addEventListener('mouseout', (e) => {
if (button && !video.contains(e.relatedTarget) && !button.contains(e.relatedTarget)) {
button.remove();
button = null;
}
});
};
const checkForNewVideos = () => {
document.querySelectorAll('video:not(.processed)').forEach((video) => {
video.classList.add('processed');
manageDownloadButtons(video);
});
requestAnimationFrame(checkForNewVideos);
};
requestAnimationFrame(checkForNewVideos);
})();