Greasy Fork is available in English.
Download TikTok videos without watermark
当前为
// ==UserScript==
// @name Tiktok Video Downloader🎬
// @namespace http://greasyfork.icu/en/scripts/431826
// @version 1.8
// @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 INTERVAL_MS = 500;
// Extract video ID from the URL to name the file
const getFileName = (url) => {
const videoId = url.split('/').pop().split('?')[0];
return `TikTok_Video_${videoId}.mp4`;
};
// Function to handle video download with error handling and feedback
const downloadVideo = (url, button) => {
if (!url) {
console.error('No valid video URL found.');
button.textContent = '✖️';
return;
}
button.textContent = '⏳';
GM_xmlhttpRequest({
method: 'GET',
url,
responseType: 'blob',
onload: ({ response, status, statusText }) => {
console.log(`Response Status: ${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 {
console.error('Failed to download the video.');
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
}
},
onerror: (error) => {
console.error('An error occurred during the request.', error);
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
},
ontimeout: () => {
console.error('Request timed out.');
button.textContent = '✖️';
setTimeout(() => button.remove(), 1500);
}
});
};
// Create and style the download button
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 = '🎞️';
// On button click, initiate download and provide feedback
button.onclick = (e) => {
e.stopPropagation();
e.preventDefault();
button.style.backgroundColor = '#EF2950';
const videoUrl = video.src || video.querySelector('source')?.src;
downloadVideo(videoUrl, button);
};
// Append the button to the video container
video.parentNode.style.position = 'relative';
video.parentNode.appendChild(button);
return button;
};
// Manage button visibility and creation on hover
const manageDownloadButtons = (video) => {
let button;
video.addEventListener('mouseover', () => {
if (!button) {
button = createDownloadButton(video);
}
});
// Remove the button only after download is complete or fails
video.addEventListener('mouseout', (e) => {
if (button && !video.contains(e.relatedTarget) && !button.contains(e.relatedTarget)) {
button.remove();
button = null;
}
});
};
// Use requestAnimationFrame instead of setInterval for performance improvement
const checkForNewVideos = () => {
document.querySelectorAll('video:not(.processed)').forEach((video) => {
video.classList.add('processed');
manageDownloadButtons(video);
});
requestAnimationFrame(checkForNewVideos);
};
// Start checking for new videos
requestAnimationFrame(checkForNewVideos);
})();